mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-02 01:52:51 +00:00
Merge from rustc
This commit is contained in:
commit
f5fd967283
@ -330,6 +330,10 @@ pub trait MutVisitor: Sized {
|
||||
fn visit_capture_by(&mut self, capture_by: &mut CaptureBy) {
|
||||
walk_capture_by(self, capture_by)
|
||||
}
|
||||
|
||||
fn visit_fn_ret_ty(&mut self, fn_ret_ty: &mut FnRetTy) {
|
||||
walk_fn_ret_ty(self, fn_ret_ty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
|
||||
@ -609,7 +613,7 @@ fn walk_angle_bracketed_parameter_data<T: MutVisitor>(vis: &mut T, data: &mut An
|
||||
fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut ParenthesizedArgs) {
|
||||
let ParenthesizedArgs { inputs, output, span, inputs_span } = args;
|
||||
visit_thin_vec(inputs, |input| vis.visit_ty(input));
|
||||
walk_fn_ret_ty(vis, output);
|
||||
vis.visit_fn_ret_ty(output);
|
||||
vis.visit_span(span);
|
||||
vis.visit_span(inputs_span);
|
||||
}
|
||||
@ -911,7 +915,7 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
|
||||
fn walk_fn_decl<T: MutVisitor>(vis: &mut T, decl: &mut P<FnDecl>) {
|
||||
let FnDecl { inputs, output } = decl.deref_mut();
|
||||
inputs.flat_map_in_place(|param| vis.flat_map_param(param));
|
||||
walk_fn_ret_ty(vis, output);
|
||||
vis.visit_fn_ret_ty(output);
|
||||
}
|
||||
|
||||
fn walk_fn_ret_ty<T: MutVisitor>(vis: &mut T, fn_ret_ty: &mut FnRetTy) {
|
||||
|
@ -299,6 +299,12 @@ pub trait Visitor<'ast>: Sized {
|
||||
fn visit_coroutine_kind(&mut self, _coroutine_kind: &'ast CoroutineKind) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_fn_decl(&mut self, fn_decl: &'ast FnDecl) -> Self::Result {
|
||||
walk_fn_decl(self, fn_decl)
|
||||
}
|
||||
fn visit_qself(&mut self, qs: &'ast Option<P<QSelf>>) -> Self::Result {
|
||||
walk_qself(self, qs)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result {
|
||||
@ -434,13 +440,13 @@ impl WalkItemKind for ItemKind {
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
visit_opt!(visitor, visit_ident, rename);
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
try_visit!(visitor.visit_path(prefix, id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
@ -518,10 +524,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
|
||||
let BareFnTy { safety: _, ext: _, generic_params, decl, decl_span: _ } =
|
||||
&**function_declaration;
|
||||
walk_list!(visitor, visit_generic_param, generic_params);
|
||||
try_visit!(walk_fn_decl(visitor, decl));
|
||||
try_visit!(visitor.visit_fn_decl(decl));
|
||||
}
|
||||
TyKind::Path(maybe_qself, path) => {
|
||||
try_visit!(walk_qself(visitor, maybe_qself));
|
||||
try_visit!(visitor.visit_qself(maybe_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
}
|
||||
TyKind::Pat(ty, pat) => {
|
||||
@ -652,16 +658,16 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
|
||||
let Pat { id, kind, span: _, tokens: _ } = pattern;
|
||||
match kind {
|
||||
PatKind::TupleStruct(opt_qself, path, elems) => {
|
||||
try_visit!(walk_qself(visitor, opt_qself));
|
||||
try_visit!(visitor.visit_qself(opt_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
walk_list!(visitor, visit_pat, elems);
|
||||
}
|
||||
PatKind::Path(opt_qself, path) => {
|
||||
try_visit!(walk_qself(visitor, opt_qself));
|
||||
try_visit!(visitor.visit_qself(opt_qself));
|
||||
try_visit!(visitor.visit_path(path, *id))
|
||||
}
|
||||
PatKind::Struct(opt_qself, path, fields, _rest) => {
|
||||
try_visit!(walk_qself(visitor, opt_qself));
|
||||
try_visit!(visitor.visit_qself(opt_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
walk_list!(visitor, visit_pat_field, fields);
|
||||
}
|
||||
@ -846,13 +852,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
|
||||
// Identifier and visibility are visited as a part of the item.
|
||||
try_visit!(visitor.visit_fn_header(header));
|
||||
try_visit!(visitor.visit_generics(generics));
|
||||
try_visit!(walk_fn_decl(visitor, decl));
|
||||
try_visit!(visitor.visit_fn_decl(decl));
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
FnKind::Closure(binder, coroutine_kind, decl, body) => {
|
||||
try_visit!(visitor.visit_closure_binder(binder));
|
||||
visit_opt!(visitor, visit_coroutine_kind, coroutine_kind.as_ref());
|
||||
try_visit!(walk_fn_decl(visitor, decl));
|
||||
try_visit!(visitor.visit_fn_decl(decl));
|
||||
try_visit!(visitor.visit_expr(body));
|
||||
}
|
||||
}
|
||||
@ -902,13 +908,13 @@ impl WalkItemKind for AssocItemKind {
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
visit_opt!(visitor, visit_ident, rename);
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
try_visit!(visitor.visit_path(prefix, id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
@ -1023,7 +1029,7 @@ pub fn walk_inline_asm_sym<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
InlineAsmSym { id, qself, path }: &'a InlineAsmSym,
|
||||
) -> V::Result {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
visitor.visit_path(path, *id)
|
||||
}
|
||||
|
||||
@ -1055,7 +1061,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||
}
|
||||
ExprKind::Struct(se) => {
|
||||
let StructExpr { qself, path, fields, rest } = &**se;
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
walk_list!(visitor, visit_expr_field, fields);
|
||||
match rest {
|
||||
@ -1164,7 +1170,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||
}
|
||||
ExprKind::Underscore => {}
|
||||
ExprKind::Path(maybe_qself, path) => {
|
||||
try_visit!(walk_qself(visitor, maybe_qself));
|
||||
try_visit!(visitor.visit_qself(maybe_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
}
|
||||
ExprKind::Break(opt_label, opt_expr) => {
|
||||
|
@ -641,6 +641,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
|
||||
| mir::StatementKind::Coverage(..)
|
||||
| mir::StatementKind::Intrinsic(..)
|
||||
| mir::StatementKind::ConstEvalCounter
|
||||
| mir::StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| mir::StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
@ -652,6 +652,8 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
|
||||
| StatementKind::Coverage(..)
|
||||
// These do not actually affect borrowck
|
||||
| StatementKind::ConstEvalCounter
|
||||
// This do not affect borrowck
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::StorageLive(..) => {}
|
||||
StatementKind::StorageDead(local) => {
|
||||
self.access_place(
|
||||
|
@ -88,6 +88,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
|
||||
| StatementKind::Nop
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::Deinit(..)
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::SetDiscriminant { .. } => {
|
||||
bug!("Statement not allowed in this MIR phase")
|
||||
}
|
||||
|
@ -1252,6 +1252,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::Nop => {}
|
||||
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
|
||||
bug!("Statement not allowed in this MIR phase")
|
||||
@ -1727,7 +1728,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
// `Sized` bound in no way depends on precise regions, so this
|
||||
// shouldn't affect `is_sized`.
|
||||
let erased_ty = tcx.erase_regions(ty);
|
||||
if !erased_ty.is_sized(tcx, self.infcx.param_env) {
|
||||
// FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here.
|
||||
if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) {
|
||||
// in current MIR construction, all non-control-flow rvalue
|
||||
// expressions evaluate through `as_temp` or `into` a return
|
||||
// slot or local, so to find all unsized rvalues it is enough
|
||||
|
@ -38,6 +38,11 @@ local-rebuild = true
|
||||
codegen-backends = ["cranelift"]
|
||||
deny-warnings = false
|
||||
verbose-tests = false
|
||||
# The cg_clif sysroot doesn't contain llvm tools and unless llvm_tools is
|
||||
# disabled bootstrap will crash trying to copy llvm tools for the bootstrap
|
||||
# compiler.
|
||||
llvm_tools = false
|
||||
|
||||
EOF
|
||||
popd
|
||||
|
||||
|
@ -11,7 +11,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::InlineAsmMacro;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::layout::FnAbiOf;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
||||
use crate::constant::ConstantCx;
|
||||
@ -841,7 +841,7 @@ fn codegen_stmt<'tcx>(
|
||||
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
|
||||
}
|
||||
Rvalue::NullaryOp(ref null_op, ty) => {
|
||||
assert!(lval.layout().ty.is_sized(fx.tcx, ty::ParamEnv::reveal_all()));
|
||||
assert!(lval.layout().ty.is_sized(fx.tcx, fx.typing_env()));
|
||||
let layout = fx.layout_of(fx.monomorphize(ty));
|
||||
let val = match null_op {
|
||||
NullOp::SizeOf => layout.size.bytes(),
|
||||
@ -924,6 +924,7 @@ fn codegen_stmt<'tcx>(
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::AscribeUserType(..) => {}
|
||||
|
||||
StatementKind::Coverage { .. } => unreachable!(),
|
||||
|
@ -103,7 +103,7 @@ fn clif_pair_type_from_ty<'tcx>(
|
||||
|
||||
/// Is a pointer to this type a wide ptr?
|
||||
pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
if ty.is_sized(tcx, ty::ParamEnv::reveal_all()) {
|
||||
if ty.is_sized(tcx, ty::TypingEnv::fully_monomorphized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -583,6 +583,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
@ -744,7 +744,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
|
||||
let const_val = fx
|
||||
.tcx
|
||||
.const_eval_instance(ty::ParamEnv::reveal_all(), instance, source_info.span)
|
||||
.const_eval_instance(
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
instance,
|
||||
source_info.span,
|
||||
)
|
||||
.unwrap();
|
||||
let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty);
|
||||
ret.write_cvalue(fx, val);
|
||||
|
@ -1279,7 +1279,7 @@ impl<'a> WasmLd<'a> {
|
||||
let mut wasm_ld = WasmLd { cmd, sess };
|
||||
if sess.target_features.contains(&sym::atomics) {
|
||||
wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]);
|
||||
if sess.target.os == "unknown" {
|
||||
if sess.target.os == "unknown" || sess.target.os == "none" {
|
||||
wasm_ld.link_args(&[
|
||||
"--export=__wasm_init_tls",
|
||||
"--export=__tls_size",
|
||||
@ -1403,7 +1403,7 @@ impl<'a> Linker for WasmLd<'a> {
|
||||
// symbols explicitly passed via the `--export` flags above and hides all
|
||||
// others. Various bits and pieces of wasm32-unknown-unknown tooling use
|
||||
// this, so be sure these symbols make their way out of the linker as well.
|
||||
if self.sess.target.os == "unknown" {
|
||||
if self.sess.target.os == "unknown" || self.sess.target.os == "none" {
|
||||
self.link_args(&["--export=__heap_base", "--export=__data_end"]);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use rustc_middle::mir::BinOp;
|
||||
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypingMode};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
|
||||
use rustc_span::symbol::sym;
|
||||
@ -119,7 +119,8 @@ pub fn validate_trivial_unsize<'tcx>(
|
||||
) -> bool {
|
||||
match (source_data.principal(), target_data.principal()) {
|
||||
(Some(hr_source_principal), Some(hr_target_principal)) => {
|
||||
let infcx = tcx.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||
let (infcx, param_env) =
|
||||
tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
|
||||
let universe = infcx.universe();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
infcx.enter_forall(hr_target_principal, |target_principal| {
|
||||
@ -130,7 +131,7 @@ pub fn validate_trivial_unsize<'tcx>(
|
||||
);
|
||||
let Ok(()) = ocx.eq_trace(
|
||||
&ObligationCause::dummy(),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
param_env,
|
||||
ToTrace::to_trace(
|
||||
&ObligationCause::dummy(),
|
||||
hr_target_principal,
|
||||
|
@ -146,10 +146,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
| sym::type_id
|
||||
| sym::type_name
|
||||
| sym::variant_count => {
|
||||
let value = bx
|
||||
.tcx()
|
||||
.const_eval_instance(ty::ParamEnv::reveal_all(), instance, span)
|
||||
.unwrap();
|
||||
let value = bx.tcx().const_eval_instance(bx.typing_env(), instance, span).unwrap();
|
||||
OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx)
|
||||
}
|
||||
sym::arith_offset => {
|
||||
|
@ -92,6 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
| mir::StatementKind::AscribeUserType(..)
|
||||
| mir::StatementKind::ConstEvalCounter
|
||||
| mir::StatementKind::PlaceMention(..)
|
||||
| mir::StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| mir::StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
@ -78,15 +78,15 @@ pub trait DerivedTypeCodegenMethods<'tcx>:
|
||||
}
|
||||
|
||||
fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_sized(self.tcx(), ty::ParamEnv::reveal_all())
|
||||
ty.is_sized(self.tcx(), self.typing_env())
|
||||
}
|
||||
|
||||
fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_freeze(self.tcx(), ty::ParamEnv::reveal_all())
|
||||
ty.is_freeze(self.tcx(), self.typing_env())
|
||||
}
|
||||
|
||||
fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool {
|
||||
if ty.is_sized(self.tcx(), self.param_env()) {
|
||||
if ty.is_sized(self.tcx(), self.typing_env()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -609,6 +609,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +120,7 @@ where
|
||||
///
|
||||
/// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
|
||||
fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool {
|
||||
!place
|
||||
.ty(self.ccx.body, self.ccx.tcx)
|
||||
.ty
|
||||
.is_freeze(self.ccx.tcx, self.ccx.typing_env.param_env)
|
||||
!place.ty(self.ccx.body, self.ccx.tcx).ty.is_freeze(self.ccx.tcx, self.ccx.typing_env)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,7 @@ use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
|
||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
@ -31,7 +30,6 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
||||
cid: GlobalId<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
) -> InterpResult<'tcx, R> {
|
||||
trace!(?ecx.param_env);
|
||||
let tcx = *ecx.tcx;
|
||||
assert!(
|
||||
cid.promoted.is_some()
|
||||
@ -126,14 +124,14 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
||||
pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
root_span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
can_access_mut_global: CanAccessMutGlobal,
|
||||
) -> CompileTimeInterpCx<'tcx> {
|
||||
debug!("mk_eval_cx: {:?}", param_env);
|
||||
debug!("mk_eval_cx: {:?}", typing_env);
|
||||
InterpCx::new(
|
||||
tcx,
|
||||
root_span,
|
||||
param_env,
|
||||
typing_env,
|
||||
CompileTimeMachine::new(can_access_mut_global, CheckAlignment::No),
|
||||
)
|
||||
}
|
||||
@ -142,11 +140,11 @@ pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
|
||||
/// Returns both the context and an `OpTy` that represents the constant.
|
||||
pub fn mk_eval_cx_for_const_val<'tcx>(
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
val: mir::ConstValue<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
|
||||
let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
|
||||
let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, typing_env, CanAccessMutGlobal::No);
|
||||
// FIXME: is it a problem to discard the error here?
|
||||
let op = ecx.const_val_to_op(val, ty, None).discard_err()?;
|
||||
Some((ecx, op))
|
||||
@ -245,7 +243,7 @@ pub(super) fn op_to_const<'tcx>(
|
||||
pub(crate) fn turn_into_const_value<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
constant: ConstAlloc<'tcx>,
|
||||
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
|
||||
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
|
||||
) -> ConstValue<'tcx> {
|
||||
let cid = key.value;
|
||||
let def_id = cid.instance.def.def_id();
|
||||
@ -254,7 +252,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
|
||||
let ecx = mk_eval_cx_to_read_const_val(
|
||||
tcx,
|
||||
tcx.def_span(key.value.instance.def_id()),
|
||||
key.param_env,
|
||||
key.typing_env,
|
||||
CanAccessMutGlobal::from(is_static),
|
||||
);
|
||||
|
||||
@ -274,23 +272,16 @@ pub(crate) fn turn_into_const_value<'tcx>(
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn eval_to_const_value_raw_provider<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
|
||||
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
|
||||
) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
|
||||
// Const eval always happens in Reveal::All mode in order to be able to use the hidden types of
|
||||
// opaque types. This is needed for trivial things like `size_of`, but also for using associated
|
||||
// types that are not specified in the opaque type.
|
||||
assert_eq!(key.param_env.reveal(), Reveal::All);
|
||||
let typing_env =
|
||||
ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env: key.param_env };
|
||||
|
||||
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
|
||||
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
|
||||
if let ty::InstanceKind::Intrinsic(def_id) = key.value.instance.def {
|
||||
let ty = key.value.instance.ty(tcx, typing_env);
|
||||
let ty = key.value.instance.ty(tcx, key.typing_env);
|
||||
let ty::FnDef(_, args) = ty.kind() else {
|
||||
bug!("intrinsic with type {:?}", ty);
|
||||
};
|
||||
return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).report_err().map_err(
|
||||
return eval_nullary_intrinsic(tcx, key.typing_env, def_id, args).report_err().map_err(
|
||||
|error| {
|
||||
let span = tcx.def_span(def_id);
|
||||
|
||||
@ -317,7 +308,7 @@ pub fn eval_static_initializer_provider<'tcx>(
|
||||
|
||||
let instance = ty::Instance::mono(tcx, def_id.to_def_id());
|
||||
let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
|
||||
eval_in_interpreter(tcx, cid, ty::ParamEnv::reveal_all())
|
||||
eval_in_interpreter(tcx, cid, ty::TypingEnv::fully_monomorphized())
|
||||
}
|
||||
|
||||
pub trait InterpretationResult<'tcx> {
|
||||
@ -342,16 +333,14 @@ impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn eval_to_allocation_raw_provider<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
|
||||
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
|
||||
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
|
||||
// This shouldn't be used for statics, since statics are conceptually places,
|
||||
// not values -- so what we do here could break pointer identity.
|
||||
assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id()));
|
||||
// Const eval always happens in Reveal::All mode in order to be able to use the hidden types of
|
||||
// opaque types. This is needed for trivial things like `size_of`, but also for using associated
|
||||
// types that are not specified in the opaque type.
|
||||
|
||||
assert_eq!(key.param_env.reveal(), Reveal::All);
|
||||
// Const eval always happens in PostAnalysis mode . See the comment in
|
||||
// `InterpCx::new` for more details.
|
||||
debug_assert_eq!(key.typing_env.typing_mode, ty::TypingMode::PostAnalysis);
|
||||
if cfg!(debug_assertions) {
|
||||
// Make sure we format the instance even if we do not print it.
|
||||
// This serves as a regression test against an ICE on printing.
|
||||
@ -362,13 +351,13 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
||||
trace!("const eval: {:?} ({})", key, instance);
|
||||
}
|
||||
|
||||
eval_in_interpreter(tcx, key.value, key.param_env)
|
||||
eval_in_interpreter(tcx, key.value, key.typing_env)
|
||||
}
|
||||
|
||||
fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> Result<R, ErrorHandled> {
|
||||
let def = cid.instance.def.def_id();
|
||||
let is_static = tcx.is_static(def);
|
||||
@ -376,7 +365,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
|
||||
let mut ecx = InterpCx::new(
|
||||
tcx,
|
||||
tcx.def_span(def),
|
||||
param_env,
|
||||
typing_env,
|
||||
// Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
|
||||
// they do not have to behave "as if" they were evaluated at runtime.
|
||||
// For consts however we want to ensure they behave "as if" they were evaluated at runtime,
|
||||
|
@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::Span;
|
||||
@ -667,7 +667,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
||||
.is_some_and(|p| !p.immutable())
|
||||
{
|
||||
// That next check is expensive, that's why we have all the guards above.
|
||||
let is_immutable = ty.is_freeze(*ecx.tcx, ecx.param_env);
|
||||
let is_immutable = ty.is_freeze(*ecx.tcx, ecx.typing_env());
|
||||
let place = ecx.ref_to_mplace(val)?;
|
||||
let new_place = if is_immutable {
|
||||
place.map_provenance(CtfeProvenance::as_immutable)
|
||||
|
@ -38,8 +38,8 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
|
||||
val: mir::ConstValue<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<mir::DestructuredConstant<'tcx>> {
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let (ecx, op) = mk_eval_cx_for_const_val(tcx, param_env, val, ty)?;
|
||||
let typing_env = ty::TypingEnv::fully_monomorphized();
|
||||
let (ecx, op) = mk_eval_cx_for_const_val(tcx, typing_env, val, ty)?;
|
||||
|
||||
// We go to `usize` as we cannot allocate anything bigger anyway.
|
||||
let (field_count, variant, down) = match ty.kind() {
|
||||
@ -76,10 +76,12 @@ pub fn tag_for_variant_provider<'tcx>(
|
||||
) -> Option<ty::ScalarInt> {
|
||||
assert!(ty.is_enum());
|
||||
|
||||
// FIXME: This uses an empty `TypingEnv` even though
|
||||
// it may be used by a generic CTFE.
|
||||
let ecx = InterpCx::new(
|
||||
tcx,
|
||||
ty.default_span(tcx),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
crate::const_eval::DummyMachine,
|
||||
);
|
||||
|
||||
|
@ -2,7 +2,6 @@ use rustc_abi::{BackendRepr, VariantIdx};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::solve::Reveal;
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::DUMMY_SP;
|
||||
@ -229,16 +228,19 @@ fn create_valtree_place<'tcx>(
|
||||
/// Evaluates a constant and turns it into a type-level constant value.
|
||||
pub(crate) fn eval_to_valtree<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
) -> EvalToValTreeResult<'tcx> {
|
||||
let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
|
||||
// Const eval always happens in PostAnalysis mode . See the comment in
|
||||
// `InterpCx::new` for more details.
|
||||
debug_assert_eq!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
|
||||
let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?;
|
||||
|
||||
// FIXME Need to provide a span to `eval_to_valtree`
|
||||
let ecx = mk_eval_cx_to_read_const_val(
|
||||
tcx,
|
||||
DUMMY_SP,
|
||||
param_env,
|
||||
typing_env,
|
||||
// It is absolutely crucial for soundness that
|
||||
// we do not read from mutable memory.
|
||||
CanAccessMutGlobal::No,
|
||||
@ -273,7 +275,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
||||
#[instrument(skip(tcx), level = "debug", ret)]
|
||||
pub fn valtree_to_const_value<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
valtree: ty::ValTree<'tcx>,
|
||||
) -> mir::ConstValue<'tcx> {
|
||||
// Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
|
||||
@ -282,10 +285,6 @@ pub fn valtree_to_const_value<'tcx>(
|
||||
// the `ValTree` and using `place_projection` and `place_field` to
|
||||
// create inner `MPlace`s which are filled recursively.
|
||||
// FIXME Does this need an example?
|
||||
let (param_env, ty) = param_env_ty.into_parts();
|
||||
debug_assert_eq!(param_env.reveal(), Reveal::All);
|
||||
let typing_env = ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env };
|
||||
|
||||
match *ty.kind() {
|
||||
ty::FnDef(..) => {
|
||||
assert!(valtree.unwrap_branch().is_empty());
|
||||
@ -299,10 +298,10 @@ pub fn valtree_to_const_value<'tcx>(
|
||||
),
|
||||
}
|
||||
}
|
||||
ty::Pat(ty, _) => valtree_to_const_value(tcx, param_env.and(ty), valtree),
|
||||
ty::Pat(ty, _) => valtree_to_const_value(tcx, typing_env, ty, valtree),
|
||||
ty::Ref(_, inner_ty, _) => {
|
||||
let mut ecx =
|
||||
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
|
||||
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No);
|
||||
let imm = valtree_to_ref(&mut ecx, valtree, inner_ty);
|
||||
let imm =
|
||||
ImmTy::from_immediate(imm, tcx.layout_of(typing_env.as_query_input(ty)).unwrap());
|
||||
@ -324,14 +323,14 @@ pub fn valtree_to_const_value<'tcx>(
|
||||
for (i, &inner_valtree) in branches.iter().enumerate() {
|
||||
let field = layout.field(&LayoutCx::new(tcx, typing_env), i);
|
||||
if !field.is_zst() {
|
||||
return valtree_to_const_value(tcx, param_env.and(field.ty), inner_valtree);
|
||||
return valtree_to_const_value(tcx, typing_env, field.ty, inner_valtree);
|
||||
}
|
||||
}
|
||||
bug!("could not find non-ZST field during in {layout:#?}");
|
||||
}
|
||||
|
||||
let mut ecx =
|
||||
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
|
||||
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No);
|
||||
|
||||
// Need to create a place for this valtree.
|
||||
let place = create_valtree_place(&mut ecx, layout, valtree);
|
||||
|
@ -215,7 +215,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
// Even if `ty` is normalized, the search for the unsized tail will project
|
||||
// to fields, which can yield non-normalized types. So we need to provide a
|
||||
// normalization function.
|
||||
let normalize = |ty| self.tcx.normalize_erasing_regions(self.typing_env(), ty);
|
||||
let normalize = |ty| self.tcx.normalize_erasing_regions(self.typing_env, ty);
|
||||
ty.ptr_metadata_ty(*self.tcx, normalize)
|
||||
};
|
||||
return interp_ok(meta_ty(caller) == meta_ty(callee));
|
||||
@ -652,35 +652,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
};
|
||||
|
||||
// Obtain the underlying trait we are working on, and the adjusted receiver argument.
|
||||
let (trait_, dyn_ty, adjusted_recv) =
|
||||
if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() {
|
||||
let recv = self.unpack_dyn_star(&receiver_place, data)?;
|
||||
let (trait_, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) =
|
||||
receiver_place.layout.ty.kind()
|
||||
{
|
||||
let recv = self.unpack_dyn_star(&receiver_place, data)?;
|
||||
|
||||
(data.principal(), recv.layout.ty, recv.ptr())
|
||||
} else {
|
||||
// Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`.
|
||||
// (For that reason we also cannot use `unpack_dyn_trait`.)
|
||||
let receiver_tail = self
|
||||
.tcx
|
||||
.struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env());
|
||||
let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"dynamic call on non-`dyn` type {}",
|
||||
receiver_tail
|
||||
)
|
||||
};
|
||||
assert!(receiver_place.layout.is_unsized());
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?;
|
||||
let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?;
|
||||
|
||||
// It might be surprising that we use a pointer as the receiver even if this
|
||||
// is a by-val case; this works because by-val passing of an unsized `dyn
|
||||
// Trait` to a function is actually desugared to a pointer.
|
||||
(receiver_trait.principal(), dyn_ty, receiver_place.ptr())
|
||||
(data.principal(), recv.layout.ty, recv.ptr())
|
||||
} else {
|
||||
// Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`.
|
||||
// (For that reason we also cannot use `unpack_dyn_trait`.)
|
||||
let receiver_tail =
|
||||
self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env);
|
||||
let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"dynamic call on non-`dyn` type {}",
|
||||
receiver_tail
|
||||
)
|
||||
};
|
||||
assert!(receiver_place.layout.is_unsized());
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?;
|
||||
let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?;
|
||||
|
||||
// It might be surprising that we use a pointer as the receiver even if this
|
||||
// is a by-val case; this works because by-val passing of an unsized `dyn
|
||||
// Trait` to a function is actually desugared to a pointer.
|
||||
(receiver_trait.principal(), dyn_ty, receiver_place.ptr())
|
||||
};
|
||||
|
||||
// Now determine the actual method to call. Usually we use the easy way of just
|
||||
// looking up the method at index `idx`.
|
||||
@ -704,7 +704,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
let concrete_method = Instance::expect_resolve_for_vtable(
|
||||
tcx,
|
||||
self.typing_env(),
|
||||
self.typing_env,
|
||||
def_id,
|
||||
instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
|
||||
self.cur_span(),
|
||||
|
@ -83,7 +83,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
ty::FnDef(def_id, args) => {
|
||||
let instance = ty::Instance::resolve_for_fn_ptr(
|
||||
*self.tcx,
|
||||
self.typing_env(),
|
||||
self.typing_env,
|
||||
def_id,
|
||||
args,
|
||||
)
|
||||
@ -384,7 +384,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
) -> InterpResult<'tcx> {
|
||||
// A<Struct> -> A<Trait> conversion
|
||||
let (src_pointee_ty, dest_pointee_ty) =
|
||||
self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.typing_env());
|
||||
self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.typing_env);
|
||||
|
||||
match (src_pointee_ty.kind(), dest_pointee_ty.kind()) {
|
||||
(&ty::Array(_, length), &ty::Slice(_)) => {
|
||||
|
@ -1,10 +1,12 @@
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::traits::{ObligationCause, Reveal};
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{
|
||||
@ -36,8 +38,9 @@ pub struct InterpCx<'tcx, M: Machine<'tcx>> {
|
||||
/// we are evaluating (if this is CTFE).
|
||||
pub tcx: TyCtxtAt<'tcx>,
|
||||
|
||||
/// Bounds in scope for polymorphic evaluations.
|
||||
pub(crate) param_env: ty::ParamEnv<'tcx>,
|
||||
/// The current context in case we're evaluating in a
|
||||
/// polymorphic context. This always uses `ty::TypingMode::PostAnalysis`.
|
||||
pub(super) typing_env: ty::TypingEnv<'tcx>,
|
||||
|
||||
/// The virtual memory system.
|
||||
pub memory: Memory<'tcx, M>,
|
||||
@ -68,7 +71,7 @@ where
|
||||
M: Machine<'tcx>,
|
||||
{
|
||||
fn typing_env(&self) -> ty::TypingEnv<'tcx> {
|
||||
self.typing_env()
|
||||
self.typing_env
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,25 +192,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
root_span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
machine: M,
|
||||
) -> Self {
|
||||
// Const eval always happens in post analysis mode in order to be able to use the hidden types of
|
||||
// opaque types. This is needed for trivial things like `size_of`, but also for using associated
|
||||
// types that are not specified in the opaque type. We also use MIR bodies whose opaque types have
|
||||
// already been revealed, so we'd be able to at least partially observe the hidden types anyways.
|
||||
debug_assert_matches!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
|
||||
InterpCx {
|
||||
machine,
|
||||
tcx: tcx.at(root_span),
|
||||
param_env,
|
||||
typing_env,
|
||||
memory: Memory::new(),
|
||||
recursion_limit: tcx.recursion_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// During CTFE we're always in `PostAnalysis` mode.
|
||||
#[inline(always)]
|
||||
pub fn typing_env(&self) -> ty::TypingEnv<'tcx> {
|
||||
debug_assert_eq!(self.param_env.reveal(), Reveal::All);
|
||||
ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env: self.param_env }
|
||||
}
|
||||
|
||||
/// Returns the span of the currently executed statement/terminator.
|
||||
/// This is the span typically used for error reporting.
|
||||
#[inline(always)]
|
||||
@ -250,7 +251,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
#[inline]
|
||||
pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_freeze(*self.tcx, self.param_env)
|
||||
ty.is_freeze(*self.tcx, self.typing_env)
|
||||
}
|
||||
|
||||
pub fn load_mir(
|
||||
@ -296,7 +297,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
.instance
|
||||
.try_instantiate_mir_and_normalize_erasing_regions(
|
||||
*self.tcx,
|
||||
self.typing_env(),
|
||||
self.typing_env,
|
||||
ty::EarlyBinder::bind(value),
|
||||
)
|
||||
.map_err(|_| ErrorHandled::TooGeneric(self.cur_span()))
|
||||
@ -309,9 +310,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
args: GenericArgsRef<'tcx>,
|
||||
) -> InterpResult<'tcx, ty::Instance<'tcx>> {
|
||||
trace!("resolve: {:?}, {:#?}", def, args);
|
||||
trace!("param_env: {:#?}", self.param_env);
|
||||
trace!("typing_env: {:#?}", self.typing_env);
|
||||
trace!("args: {:#?}", args);
|
||||
match ty::Instance::try_resolve(*self.tcx, self.typing_env(), def, args) {
|
||||
match ty::Instance::try_resolve(*self.tcx, self.typing_env, def, args) {
|
||||
Ok(Some(instance)) => interp_ok(instance),
|
||||
Ok(None) => throw_inval!(TooGeneric),
|
||||
|
||||
@ -332,7 +333,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
return true;
|
||||
}
|
||||
// Slow path: spin up an inference context to check if these traits are sufficiently equal.
|
||||
let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env());
|
||||
let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let cause = ObligationCause::dummy_with_span(self.cur_span());
|
||||
// equate the two trait refs after normalization
|
||||
@ -564,10 +565,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
let val = if self.tcx.is_static(gid.instance.def_id()) {
|
||||
let alloc_id = self.tcx.reserve_and_set_static_alloc(gid.instance.def_id());
|
||||
|
||||
let ty = instance.ty(self.tcx.tcx, self.typing_env());
|
||||
let ty = instance.ty(self.tcx.tcx, self.typing_env);
|
||||
mir::ConstAlloc { alloc_id, ty }
|
||||
} else {
|
||||
self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.param_env.and(gid)))?
|
||||
self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.typing_env.as_query_input(gid)))?
|
||||
};
|
||||
self.raw_const_to_mplace(val)
|
||||
}
|
||||
@ -579,7 +580,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
|
||||
let const_val = val.eval(*ecx.tcx, ecx.typing_env(), span).map_err(|err| {
|
||||
let const_val = val.eval(*ecx.tcx, ecx.typing_env, span).map_err(|err| {
|
||||
if M::ALL_CONSTS_ARE_PRECHECKED {
|
||||
match err {
|
||||
ErrorHandled::TooGeneric(..) => {},
|
||||
|
@ -165,7 +165,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
||||
InternKind::Static(Mutability::Not) => {
|
||||
(
|
||||
// Outermost allocation is mutable if `!Freeze`.
|
||||
if ret.layout.ty.is_freeze(*ecx.tcx, ecx.param_env) {
|
||||
if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) {
|
||||
Mutability::Not
|
||||
} else {
|
||||
Mutability::Mut
|
||||
|
@ -34,13 +34,12 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
|
||||
/// inside an `InterpCx` and instead have their value computed directly from rustc internal info.
|
||||
pub(crate) fn eval_nullary_intrinsic<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
def_id: DefId,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
) -> InterpResult<'tcx, ConstValue<'tcx>> {
|
||||
let tp_ty = args.type_at(0);
|
||||
let name = tcx.item_name(def_id);
|
||||
let typing_env = ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env };
|
||||
interp_ok(match name {
|
||||
sym::type_name => {
|
||||
ensure_monomorphic_enough(tcx, tp_ty)?;
|
||||
@ -152,8 +151,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
sym::type_name => Ty::new_static_str(self.tcx.tcx),
|
||||
_ => bug!(),
|
||||
};
|
||||
let val =
|
||||
self.ctfe_query(|tcx| tcx.const_eval_global_id(self.param_env, gid, tcx.span))?;
|
||||
let val = self
|
||||
.ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?;
|
||||
let val = self.const_val_to_op(val, ty, Some(dest.layout))?;
|
||||
self.copy_op(&val, dest)?;
|
||||
}
|
||||
@ -358,7 +357,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
let should_panic = !self
|
||||
.tcx
|
||||
.check_validity_requirement((requirement, self.typing_env().as_query_input(ty)))
|
||||
.check_validity_requirement((requirement, self.typing_env.as_query_input(ty)))
|
||||
.map_err(|_| err_inval!(TooGeneric))?;
|
||||
|
||||
if should_panic {
|
||||
|
@ -859,8 +859,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
||||
// # Global allocations
|
||||
if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) {
|
||||
let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env());
|
||||
let mutbl = global_alloc.mutability(*self.tcx, self.param_env);
|
||||
let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
|
||||
let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
|
||||
let kind = match global_alloc {
|
||||
GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
|
||||
GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
|
||||
|
@ -533,7 +533,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
}
|
||||
OffsetOf(fields) => {
|
||||
let val =
|
||||
self.tcx.offset_of_subfield(self.typing_env(), layout, fields.iter()).bytes();
|
||||
self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes();
|
||||
ImmTy::from_uint(val, usize_layout())
|
||||
}
|
||||
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
|
||||
|
@ -540,7 +540,7 @@ where
|
||||
)?;
|
||||
if !mir_assign_valid_types(
|
||||
*self.tcx,
|
||||
self.typing_env(),
|
||||
self.typing_env,
|
||||
self.layout_of(normalized_place_ty)?,
|
||||
place.layout,
|
||||
) {
|
||||
@ -871,7 +871,7 @@ where
|
||||
// We do NOT compare the types for equality, because well-typed code can
|
||||
// actually "transmute" `&mut T` to `&T` in an assignment without a cast.
|
||||
let layout_compat =
|
||||
mir_assign_valid_types(*self.tcx, self.typing_env(), src.layout(), dest.layout());
|
||||
mir_assign_valid_types(*self.tcx, self.typing_env, src.layout(), dest.layout());
|
||||
if !allow_transmute && !layout_compat {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
|
@ -379,7 +379,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
for &const_ in body.required_consts() {
|
||||
let c =
|
||||
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
|
||||
c.eval(*self.tcx, self.typing_env(), const_.span).map_err(|err| {
|
||||
c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| {
|
||||
err.emit_note(*self.tcx);
|
||||
err
|
||||
})?;
|
||||
@ -596,7 +596,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
return interp_ok(layout);
|
||||
}
|
||||
|
||||
let layout = from_known_layout(self.tcx, self.typing_env(), layout, || {
|
||||
let layout = from_known_layout(self.tcx, self.typing_env, layout, || {
|
||||
let local_ty = frame.body.local_decls[local].ty;
|
||||
let local_ty =
|
||||
self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
|
||||
|
@ -143,6 +143,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
// Defined to do nothing. These are added by optimization passes, to avoid changing the
|
||||
// size of MIR constantly.
|
||||
Nop => {}
|
||||
|
||||
// Only used for temporary lifetime lints
|
||||
BackwardIncompatibleDropHint { .. } => {}
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
@ -418,8 +421,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
||||
|
||||
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
|
||||
let fn_sig =
|
||||
self.tcx.normalize_erasing_late_bound_regions(self.typing_env(), fn_sig_binder);
|
||||
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, fn_sig_binder);
|
||||
let extra_args = &args[fn_sig.inputs().len()..];
|
||||
let extra_args =
|
||||
self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty));
|
||||
|
@ -448,7 +448,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
meta: MemPlaceMeta<M::Provenance>,
|
||||
pointee: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env());
|
||||
let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env);
|
||||
match tail.kind() {
|
||||
ty::Dynamic(data, _, ty::Dyn) => {
|
||||
let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
|
||||
@ -568,7 +568,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind });
|
||||
};
|
||||
let (size, _align) =
|
||||
global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env());
|
||||
global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env);
|
||||
|
||||
if let GlobalAlloc::Static(did) = global_alloc {
|
||||
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
|
||||
@ -619,7 +619,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
};
|
||||
// Determine what it actually points to.
|
||||
let alloc_actual_mutbl =
|
||||
global_alloc.mutability(*self.ecx.tcx, self.ecx.param_env);
|
||||
global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
|
||||
// Mutable pointer to immutable memory is no good.
|
||||
if ptr_expected_mutbl == Mutability::Mut
|
||||
&& alloc_actual_mutbl == Mutability::Not
|
||||
@ -848,7 +848,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
|
||||
let tcx = *self.ecx.tcx;
|
||||
// Everything must be already interned.
|
||||
let mutbl = tcx.global_alloc(alloc_id).mutability(tcx, self.ecx.param_env);
|
||||
let mutbl = tcx.global_alloc(alloc_id).mutability(tcx, self.ecx.typing_env);
|
||||
if let Some((_, alloc)) = self.ecx.memory.alloc_map.get(alloc_id) {
|
||||
assert_eq!(alloc.mutability, mutbl);
|
||||
}
|
||||
@ -955,7 +955,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
) -> Cow<'e, RangeSet> {
|
||||
assert!(layout.ty.is_union());
|
||||
assert!(layout.is_sized(), "there are no unsized unions");
|
||||
let layout_cx = LayoutCx::new(*ecx.tcx, ecx.typing_env());
|
||||
let layout_cx = LayoutCx::new(*ecx.tcx, ecx.typing_env);
|
||||
return M::cached_union_data_range(ecx, layout.ty, || {
|
||||
let mut out = RangeSet(Vec::new());
|
||||
union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out);
|
||||
@ -1085,7 +1085,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory.
|
||||
if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
|
||||
if !val.layout.is_zst() && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
|
||||
if !val.layout.is_zst() && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.typing_env)
|
||||
{
|
||||
if !self.in_mutable_memory(val) {
|
||||
throw_validation_failure!(self.path, UnsafeCellInImmutable);
|
||||
}
|
||||
|
@ -40,14 +40,13 @@ pub fn provide(providers: &mut Providers) {
|
||||
providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
|
||||
providers.eval_static_initializer = const_eval::eval_static_initializer_provider;
|
||||
providers.hooks.const_caller_location = util::caller_location::const_caller_location_provider;
|
||||
providers.eval_to_valtree = |tcx, param_env_and_value| {
|
||||
let (param_env, raw) = param_env_and_value.into_parts();
|
||||
const_eval::eval_to_valtree(tcx, param_env, raw)
|
||||
providers.eval_to_valtree = |tcx, ty::PseudoCanonicalInput { typing_env, value }| {
|
||||
const_eval::eval_to_valtree(tcx, typing_env, value)
|
||||
};
|
||||
providers.hooks.try_destructure_mir_constant_for_user_output =
|
||||
const_eval::try_destructure_mir_constant_for_user_output;
|
||||
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
|
||||
const_eval::valtree_to_const_value(tcx, ty::ParamEnv::reveal_all().and(ty), valtree)
|
||||
const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree)
|
||||
};
|
||||
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
|
||||
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
|
||||
|
@ -60,7 +60,7 @@ pub(crate) fn const_caller_location_provider(
|
||||
let mut ecx = mk_eval_cx_to_read_const_val(
|
||||
tcx.tcx,
|
||||
tcx.span,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
CanAccessMutGlobal::No,
|
||||
);
|
||||
|
||||
|
@ -49,7 +49,7 @@ fn check_validity_requirement_strict<'tcx>(
|
||||
) -> Result<bool, &'tcx LayoutError<'tcx>> {
|
||||
let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
|
||||
|
||||
let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.typing_env.param_env, machine);
|
||||
let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.typing_env, machine);
|
||||
|
||||
let allocated = cx
|
||||
.allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
|
||||
|
@ -602,6 +602,11 @@ impl<K: Eq + Hash, V> UnordMap<K, V> {
|
||||
.into_iter()
|
||||
.map(|(_, v)| v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, Q: ?Sized, V> Index<&Q> for UnordMap<K, V>
|
||||
|
@ -577,7 +577,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) {
|
||||
let mut cmd = Command::new(&pager_name);
|
||||
// FIXME: find if other pagers accept color options
|
||||
let mut print_formatted = if pager_name == "less" {
|
||||
cmd.arg("-r");
|
||||
cmd.arg("-R");
|
||||
true
|
||||
} else {
|
||||
["bat", "catbat", "delta"].iter().any(|v| *v == pager_name)
|
||||
|
@ -879,6 +879,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, EncodeCrossCrate::No, lang_items,
|
||||
"lang items are subject to change",
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_as_ptr, Normal, template!(Word), ErrorFollowing,
|
||||
EncodeCrossCrate::Yes,
|
||||
"#[rustc_as_ptr] is used to mark functions returning pointers to their inner allocations."
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
|
||||
EncodeCrossCrate::Yes,
|
||||
|
@ -90,24 +90,24 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
|
||||
fn allowed_union_field<'tcx>(
|
||||
ty: Ty<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> bool {
|
||||
// We don't just accept all !needs_drop fields, due to semver concerns.
|
||||
match ty.kind() {
|
||||
ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
|
||||
ty::Tuple(tys) => {
|
||||
// allow tuples of allowed types
|
||||
tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env))
|
||||
tys.iter().all(|ty| allowed_union_field(ty, tcx, typing_env))
|
||||
}
|
||||
ty::Array(elem, _len) => {
|
||||
// Like `Copy`, we do *not* special-case length 0.
|
||||
allowed_union_field(*elem, tcx, param_env)
|
||||
allowed_union_field(*elem, tcx, typing_env)
|
||||
}
|
||||
_ => {
|
||||
// Fallback case: allow `ManuallyDrop` and things that are `Copy`,
|
||||
// also no need to report an error if the type is unresolved.
|
||||
ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
|
||||
|| ty.is_copy_modulo_regions(tcx, param_env)
|
||||
|| ty.is_copy_modulo_regions(tcx, typing_env)
|
||||
|| ty.references_error()
|
||||
}
|
||||
}
|
||||
@ -121,7 +121,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
|
||||
continue;
|
||||
};
|
||||
|
||||
if !allowed_union_field(field_ty, tcx, typing_env.param_env) {
|
||||
if !allowed_union_field(field_ty, tcx, typing_env) {
|
||||
let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
|
||||
// We are currently checking the type this field came from, so it must be local.
|
||||
Some(Node::Field(field)) => (field.span, field.ty.span),
|
||||
|
@ -1193,9 +1193,9 @@ fn compare_self_type<'tcx>(
|
||||
ty::AssocItemContainer::Trait => tcx.types.self_param,
|
||||
};
|
||||
let self_arg_ty = tcx.fn_sig(method.def_id).instantiate_identity().input(0);
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
|
||||
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let (infcx, param_env) = tcx
|
||||
.infer_ctxt()
|
||||
.build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, method.def_id));
|
||||
let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
|
||||
let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty);
|
||||
match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
|
||||
|
@ -15,7 +15,7 @@ use rustc_target::asm::{
|
||||
|
||||
pub struct InlineAsmCtxt<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
|
||||
}
|
||||
|
||||
@ -23,24 +23,29 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
|
||||
pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
|
||||
InlineAsmCtxt {
|
||||
tcx,
|
||||
param_env: ty::ParamEnv::empty(),
|
||||
typing_env: ty::TypingEnv {
|
||||
typing_mode: ty::TypingMode::non_body_analysis(),
|
||||
param_env: ty::ParamEnv::empty(),
|
||||
},
|
||||
get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#132279): This likely causes us to incorrectly handle opaque types in their
|
||||
// defining scope.
|
||||
pub fn new_in_fn(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
|
||||
) -> Self {
|
||||
InlineAsmCtxt { tcx, param_env, get_operand_ty: Box::new(get_operand_ty) }
|
||||
InlineAsmCtxt { tcx, typing_env, get_operand_ty: Box::new(get_operand_ty) }
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
|
||||
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
|
||||
// Type still may have region variables, but `Sized` does not depend
|
||||
// on those, so just erase them before querying.
|
||||
if ty.is_sized(self.tcx, self.param_env) {
|
||||
if ty.is_sized(self.tcx, self.typing_env) {
|
||||
return true;
|
||||
}
|
||||
if let ty::Foreign(..) = ty.kind() {
|
||||
@ -171,7 +176,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
|
||||
|
||||
// Check that the type implements Copy. The only case where this can
|
||||
// possibly fail is for SIMD types which don't #[derive(Copy)].
|
||||
if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
|
||||
if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) {
|
||||
let msg = "arguments for inline assembly must be copyable";
|
||||
self.tcx
|
||||
.dcx()
|
||||
|
@ -17,6 +17,7 @@ use rustc_index::Idx;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::region::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint;
|
||||
use rustc_span::source_map;
|
||||
use tracing::debug;
|
||||
|
||||
@ -167,8 +168,23 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||
}
|
||||
}
|
||||
if let Some(tail_expr) = blk.expr {
|
||||
if blk.span.edition().at_least_rust_2024() {
|
||||
visitor.terminating_scopes.insert(tail_expr.hir_id.local_id);
|
||||
let local_id = tail_expr.hir_id.local_id;
|
||||
let edition = blk.span.edition();
|
||||
if edition.at_least_rust_2024() {
|
||||
visitor.terminating_scopes.insert(local_id);
|
||||
} else if !visitor
|
||||
.tcx
|
||||
.lints_that_dont_need_to_run(())
|
||||
.contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER))
|
||||
{
|
||||
// If this temporary scope will be changing once the codebase adopts Rust 2024,
|
||||
// and we are linting about possible semantic changes that would result,
|
||||
// then record this node-id in the field `backwards_incompatible_scope`
|
||||
// for future reference.
|
||||
visitor
|
||||
.scope_tree
|
||||
.backwards_incompatible_scope
|
||||
.insert(local_id, Scope { id: local_id, data: ScopeData::Node });
|
||||
}
|
||||
visitor.visit_expr(tail_expr);
|
||||
}
|
||||
|
@ -37,22 +37,19 @@ pub(super) fn check_trait<'tcx>(
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let lang_items = tcx.lang_items();
|
||||
let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
|
||||
let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop);
|
||||
res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy));
|
||||
res = res.and(checker.check(lang_items.const_param_ty_trait(), |checker| {
|
||||
checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?;
|
||||
checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;
|
||||
checker.check(lang_items.const_param_ty_trait(), |checker| {
|
||||
visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy)
|
||||
}));
|
||||
res = res.and(checker.check(lang_items.unsized_const_param_ty_trait(), |checker| {
|
||||
})?;
|
||||
checker.check(lang_items.unsized_const_param_ty_trait(), |checker| {
|
||||
visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy)
|
||||
}));
|
||||
|
||||
res = res.and(
|
||||
checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized),
|
||||
);
|
||||
res.and(
|
||||
checker
|
||||
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn),
|
||||
)
|
||||
})?;
|
||||
checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?;
|
||||
checker
|
||||
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?;
|
||||
checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct Checker<'tcx> {
|
||||
@ -663,3 +660,63 @@ fn infringing_fields_error<'tcx>(
|
||||
|
||||
err.emit()
|
||||
}
|
||||
|
||||
fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let typing_env = ty::TypingEnv::non_body_analysis(tcx, checker.impl_def_id);
|
||||
let impl_span = tcx.def_span(checker.impl_def_id);
|
||||
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
|
||||
|
||||
// If an ADT is repr(transparent)...
|
||||
if let ty::Adt(def, args) = *self_ty.kind()
|
||||
&& def.repr().transparent()
|
||||
{
|
||||
// FIXME(compiler-errors): This should and could be deduplicated into a query.
|
||||
// Find the nontrivial field.
|
||||
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did());
|
||||
let nontrivial_field = def.all_fields().find(|field_def| {
|
||||
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
|
||||
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
|
||||
.is_ok_and(|layout| layout.layout.is_1zst())
|
||||
});
|
||||
|
||||
if let Some(nontrivial_field) = nontrivial_field {
|
||||
// Check that the nontrivial field implements `PointerLike`.
|
||||
let nontrivial_field = nontrivial_field.ty(tcx, args);
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_bound(
|
||||
ObligationCause::misc(impl_span, checker.impl_def_id),
|
||||
param_env,
|
||||
nontrivial_field,
|
||||
tcx.lang_items().pointer_like().unwrap(),
|
||||
);
|
||||
// FIXME(dyn-star): We should regionck this implementation.
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_permitted_primitive = match *self_ty.kind() {
|
||||
ty::Adt(def, _) => def.is_box(),
|
||||
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_permitted_primitive
|
||||
&& let Ok(layout) = tcx.layout_of(typing_env.as_query_input(self_ty))
|
||||
&& layout.layout.is_pointer_like(&tcx.data_layout)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
impl_span,
|
||||
"implementation must be applied to type that has the same ABI as a pointer, \
|
||||
or is `repr(transparent)` and whose field is `PointerLike`",
|
||||
)
|
||||
.emit())
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ pub(crate) fn orphan_check_impl(
|
||||
// impl<T> AutoTrait for T {}
|
||||
// impl<T: ?Sized> AutoTrait for T {}
|
||||
ty::Param(..) => (
|
||||
if self_ty.is_sized(tcx, tcx.param_env(impl_def_id)) {
|
||||
if self_ty.is_sized(tcx, ty::TypingEnv::non_body_analysis(tcx, impl_def_id)) {
|
||||
LocalImpl::Allow
|
||||
} else {
|
||||
LocalImpl::Disallow { problematic_kind: "generic type" }
|
||||
|
@ -190,8 +190,8 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
|
||||
DefKind::Const if tcx.generics_of(item_def_id).is_empty() => {
|
||||
let instance = ty::Instance::new(item_def_id.into(), ty::GenericArgs::empty());
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
tcx.ensure().eval_to_const_value_raw(param_env.and(cid));
|
||||
let typing_env = ty::TypingEnv::fully_monomorphized();
|
||||
tcx.ensure().eval_to_const_value_raw(typing_env.as_query_input(cid));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
|
||||
}
|
||||
|
||||
fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_copy_modulo_regions(self.0.tcx, self.0.param_env)
|
||||
ty.is_copy_modulo_regions(self.0.tcx, self.0.typing_env())
|
||||
}
|
||||
|
||||
fn body_owner_def_id(&self) -> LocalDefId {
|
||||
|
@ -105,8 +105,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.tcx.erase_regions(ty)
|
||||
}
|
||||
};
|
||||
InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty)
|
||||
.check_asm(asm, enclosing_id);
|
||||
InlineAsmCtxt::new_in_fn(
|
||||
self.tcx,
|
||||
self.infcx.typing_env(self.param_env),
|
||||
get_operand_ty,
|
||||
)
|
||||
.check_asm(asm, enclosing_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -831,8 +831,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
|
||||
|
||||
// Normalize consts in writeback, because GCE doesn't normalize eagerly.
|
||||
if tcx.features().generic_const_exprs() {
|
||||
value =
|
||||
value.fold_with(&mut EagerlyNormalizeConsts { tcx, param_env: self.fcx.param_env });
|
||||
value = value.fold_with(&mut EagerlyNormalizeConsts::new(self.fcx));
|
||||
}
|
||||
|
||||
value
|
||||
@ -873,16 +872,22 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
|
||||
|
||||
struct EagerlyNormalizeConsts<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
}
|
||||
impl<'tcx> EagerlyNormalizeConsts<'tcx> {
|
||||
fn new(fcx: &FnCtxt<'_, 'tcx>) -> Self {
|
||||
// FIXME(#132279, generic_const_exprs): Using `try_normalize_erasing_regions` here
|
||||
// means we can't handle opaque types in their defining scope.
|
||||
EagerlyNormalizeConsts { tcx: fcx.tcx, typing_env: fcx.typing_env(fcx.param_env) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerlyNormalizeConsts<'tcx> {
|
||||
fn cx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
self.tcx
|
||||
.try_normalize_erasing_regions(ty::TypingEnv::from_param_env(self.param_env), ct)
|
||||
.unwrap_or(ct)
|
||||
self.tcx.try_normalize_erasing_regions(self.typing_env, ct).unwrap_or(ct)
|
||||
}
|
||||
}
|
||||
|
@ -460,6 +460,10 @@ impl<T: Idx> ChunkedBitSet<T> {
|
||||
self.chunks.iter().map(|chunk| chunk.count()).sum()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.chunks.iter().all(|chunk| matches!(chunk, Chunk::Zeros(..) | Chunk::Ones(0)))
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` contains `elem`.
|
||||
#[inline]
|
||||
pub fn contains(&self, elem: T) -> bool {
|
||||
@ -668,12 +672,140 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for ChunkedBitSet<T> {
|
||||
changed
|
||||
}
|
||||
|
||||
fn subtract(&mut self, _other: &ChunkedBitSet<T>) -> bool {
|
||||
unimplemented!("implement if/when necessary");
|
||||
fn subtract(&mut self, other: &ChunkedBitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
debug_assert_eq!(self.chunks.len(), other.chunks.len());
|
||||
|
||||
let mut changed = false;
|
||||
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
|
||||
match (&mut self_chunk, &other_chunk) {
|
||||
(Zeros(..), _) | (_, Zeros(..)) => {}
|
||||
(
|
||||
Ones(self_chunk_domain_size) | Mixed(self_chunk_domain_size, _, _),
|
||||
Ones(other_chunk_domain_size),
|
||||
) => {
|
||||
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
|
||||
changed = true;
|
||||
*self_chunk = Zeros(*self_chunk_domain_size);
|
||||
}
|
||||
(
|
||||
Ones(self_chunk_domain_size),
|
||||
Mixed(other_chunk_domain_size, other_chunk_count, other_chunk_words),
|
||||
) => {
|
||||
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
|
||||
changed = true;
|
||||
let num_words = num_words(*self_chunk_domain_size as usize);
|
||||
debug_assert!(num_words > 0 && num_words <= CHUNK_WORDS);
|
||||
let mut tail_mask =
|
||||
1 << (*other_chunk_domain_size - ((num_words - 1) * WORD_BITS) as u16) - 1;
|
||||
let mut self_chunk_words = **other_chunk_words;
|
||||
for word in self_chunk_words[0..num_words].iter_mut().rev() {
|
||||
*word = !*word & tail_mask;
|
||||
tail_mask = u64::MAX;
|
||||
}
|
||||
let self_chunk_count = *self_chunk_domain_size - *other_chunk_count;
|
||||
debug_assert_eq!(
|
||||
self_chunk_count,
|
||||
self_chunk_words[0..num_words]
|
||||
.iter()
|
||||
.map(|w| w.count_ones() as ChunkSize)
|
||||
.sum()
|
||||
);
|
||||
*self_chunk =
|
||||
Mixed(*self_chunk_domain_size, self_chunk_count, Rc::new(self_chunk_words));
|
||||
}
|
||||
(
|
||||
Mixed(
|
||||
self_chunk_domain_size,
|
||||
ref mut self_chunk_count,
|
||||
ref mut self_chunk_words,
|
||||
),
|
||||
Mixed(_other_chunk_domain_size, _other_chunk_count, other_chunk_words),
|
||||
) => {
|
||||
// See [`<Self as BitRelations<ChunkedBitSet<T>>>::union`] for the explanation
|
||||
let op = |a: u64, b: u64| a & !b;
|
||||
let num_words = num_words(*self_chunk_domain_size as usize);
|
||||
if bitwise_changes(
|
||||
&self_chunk_words[0..num_words],
|
||||
&other_chunk_words[0..num_words],
|
||||
op,
|
||||
) {
|
||||
let self_chunk_words = Rc::make_mut(self_chunk_words);
|
||||
let has_changed = bitwise(
|
||||
&mut self_chunk_words[0..num_words],
|
||||
&other_chunk_words[0..num_words],
|
||||
op,
|
||||
);
|
||||
debug_assert!(has_changed);
|
||||
*self_chunk_count = self_chunk_words[0..num_words]
|
||||
.iter()
|
||||
.map(|w| w.count_ones() as ChunkSize)
|
||||
.sum();
|
||||
if *self_chunk_count == 0 {
|
||||
*self_chunk = Zeros(*self_chunk_domain_size);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
fn intersect(&mut self, _other: &ChunkedBitSet<T>) -> bool {
|
||||
unimplemented!("implement if/when necessary");
|
||||
fn intersect(&mut self, other: &ChunkedBitSet<T>) -> bool {
|
||||
assert_eq!(self.domain_size, other.domain_size);
|
||||
debug_assert_eq!(self.chunks.len(), other.chunks.len());
|
||||
|
||||
let mut changed = false;
|
||||
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
|
||||
match (&mut self_chunk, &other_chunk) {
|
||||
(Zeros(..), _) | (_, Ones(..)) => {}
|
||||
(
|
||||
Ones(self_chunk_domain_size),
|
||||
Zeros(other_chunk_domain_size) | Mixed(other_chunk_domain_size, ..),
|
||||
)
|
||||
| (Mixed(self_chunk_domain_size, ..), Zeros(other_chunk_domain_size)) => {
|
||||
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
|
||||
changed = true;
|
||||
*self_chunk = other_chunk.clone();
|
||||
}
|
||||
(
|
||||
Mixed(
|
||||
self_chunk_domain_size,
|
||||
ref mut self_chunk_count,
|
||||
ref mut self_chunk_words,
|
||||
),
|
||||
Mixed(_other_chunk_domain_size, _other_chunk_count, other_chunk_words),
|
||||
) => {
|
||||
// See [`<Self as BitRelations<ChunkedBitSet<T>>>::union`] for the explanation
|
||||
let op = |a, b| a & b;
|
||||
let num_words = num_words(*self_chunk_domain_size as usize);
|
||||
if bitwise_changes(
|
||||
&self_chunk_words[0..num_words],
|
||||
&other_chunk_words[0..num_words],
|
||||
op,
|
||||
) {
|
||||
let self_chunk_words = Rc::make_mut(self_chunk_words);
|
||||
let has_changed = bitwise(
|
||||
&mut self_chunk_words[0..num_words],
|
||||
&other_chunk_words[0..num_words],
|
||||
op,
|
||||
);
|
||||
debug_assert!(has_changed);
|
||||
*self_chunk_count = self_chunk_words[0..num_words]
|
||||
.iter()
|
||||
.map(|w| w.count_ones() as ChunkSize)
|
||||
.sum();
|
||||
if *self_chunk_count == 0 {
|
||||
*self_chunk = Zeros(*self_chunk_domain_size);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changed
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,11 +57,10 @@ impl Token {
|
||||
/// Enum representing common lexeme types.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum TokenKind {
|
||||
// Multi-char tokens:
|
||||
/// "// comment"
|
||||
/// A line comment, e.g. `// comment`.
|
||||
LineComment { doc_style: Option<DocStyle> },
|
||||
|
||||
/// `/* block comment */`
|
||||
/// A block comment, e.g. `/* block comment */`.
|
||||
///
|
||||
/// Block comments can be recursive, so a sequence like `/* /* */`
|
||||
/// will not be considered terminated and will result in a parsing error.
|
||||
@ -70,18 +69,17 @@ pub enum TokenKind {
|
||||
/// Any whitespace character sequence.
|
||||
Whitespace,
|
||||
|
||||
/// "ident" or "continue"
|
||||
///
|
||||
/// At this step, keywords are also considered identifiers.
|
||||
/// An identifier or keyword, e.g. `ident` or `continue`.
|
||||
Ident,
|
||||
|
||||
/// Like the above, but containing invalid unicode codepoints.
|
||||
/// An identifier that is invalid because it contains emoji.
|
||||
InvalidIdent,
|
||||
|
||||
/// "r#ident"
|
||||
/// A raw identifier, e.g. "r#ident".
|
||||
RawIdent,
|
||||
|
||||
/// An unknown prefix, like `foo#`, `foo'`, `foo"`.
|
||||
/// An unknown literal prefix, like `foo#`, `foo'`, `foo"`. Excludes
|
||||
/// literal prefixes that contain emoji, which are considered "invalid".
|
||||
///
|
||||
/// Note that only the
|
||||
/// prefix (`foo`) is included in the token, not the separator (which is
|
||||
@ -93,87 +91,83 @@ pub enum TokenKind {
|
||||
|
||||
/// An unknown prefix in a lifetime, like `'foo#`.
|
||||
///
|
||||
/// Note that like above, only the `'` and prefix are included in the token
|
||||
/// Like `UnknownPrefix`, only the `'` and prefix are included in the token
|
||||
/// and not the separator.
|
||||
UnknownPrefixLifetime,
|
||||
|
||||
/// `'r#lt`, which in edition < 2021 is split into several tokens: `'r # lt`.
|
||||
/// A raw lifetime, e.g. `'r#foo`. In edition < 2021 it will be split into
|
||||
/// several tokens: `'r` and `#` and `foo`.
|
||||
RawLifetime,
|
||||
|
||||
/// Similar to the above, but *always* an error on every edition. This is used
|
||||
/// for emoji identifier recovery, as those are not meant to be ever accepted.
|
||||
InvalidPrefix,
|
||||
|
||||
/// Guarded string literal prefix: `#"` or `##`.
|
||||
///
|
||||
/// Used for reserving "guarded strings" (RFC 3598) in edition 2024.
|
||||
/// Split into the component tokens on older editions.
|
||||
GuardedStrPrefix,
|
||||
|
||||
/// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
|
||||
/// Literals, e.g. `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
|
||||
/// suffix, but may be present here on string and float literals. Users of
|
||||
/// this type will need to check for and reject that case.
|
||||
///
|
||||
/// See [LiteralKind] for more details.
|
||||
Literal { kind: LiteralKind, suffix_start: u32 },
|
||||
|
||||
/// "'a"
|
||||
/// A lifetime, e.g. `'a`.
|
||||
Lifetime { starts_with_number: bool },
|
||||
|
||||
// One-char tokens:
|
||||
/// ";"
|
||||
/// `;`
|
||||
Semi,
|
||||
/// ","
|
||||
/// `,`
|
||||
Comma,
|
||||
/// "."
|
||||
/// `.`
|
||||
Dot,
|
||||
/// "("
|
||||
/// `(`
|
||||
OpenParen,
|
||||
/// ")"
|
||||
/// `)`
|
||||
CloseParen,
|
||||
/// "{"
|
||||
/// `{`
|
||||
OpenBrace,
|
||||
/// "}"
|
||||
/// `}`
|
||||
CloseBrace,
|
||||
/// "["
|
||||
/// `[`
|
||||
OpenBracket,
|
||||
/// "]"
|
||||
/// `]`
|
||||
CloseBracket,
|
||||
/// "@"
|
||||
/// `@`
|
||||
At,
|
||||
/// "#"
|
||||
/// `#`
|
||||
Pound,
|
||||
/// "~"
|
||||
/// `~`
|
||||
Tilde,
|
||||
/// "?"
|
||||
/// `?`
|
||||
Question,
|
||||
/// ":"
|
||||
/// `:`
|
||||
Colon,
|
||||
/// "$"
|
||||
/// `$`
|
||||
Dollar,
|
||||
/// "="
|
||||
/// `=`
|
||||
Eq,
|
||||
/// "!"
|
||||
/// `!`
|
||||
Bang,
|
||||
/// "<"
|
||||
/// `<`
|
||||
Lt,
|
||||
/// ">"
|
||||
/// `>`
|
||||
Gt,
|
||||
/// "-"
|
||||
/// `-`
|
||||
Minus,
|
||||
/// "&"
|
||||
/// `&`
|
||||
And,
|
||||
/// "|"
|
||||
/// `|`
|
||||
Or,
|
||||
/// "+"
|
||||
/// `+`
|
||||
Plus,
|
||||
/// "*"
|
||||
/// `*`
|
||||
Star,
|
||||
/// "/"
|
||||
/// `/`
|
||||
Slash,
|
||||
/// "^"
|
||||
/// `^`
|
||||
Caret,
|
||||
/// "%"
|
||||
/// `%`
|
||||
Percent,
|
||||
|
||||
/// Unknown token, not expected by the lexer, e.g. "№"
|
||||
@ -468,7 +462,7 @@ impl Cursor<'_> {
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
// Identifier starting with an emoji. Only lexed for graceful error recovery.
|
||||
c if !c.is_ascii() && c.is_emoji_char() => self.fake_ident_or_unknown_prefix(),
|
||||
c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(),
|
||||
_ => Unknown,
|
||||
};
|
||||
let res = Token::new(token_kind, self.pos_within_token());
|
||||
@ -552,24 +546,22 @@ impl Cursor<'_> {
|
||||
// we see a prefix here, it is definitely an unknown prefix.
|
||||
match self.first() {
|
||||
'#' | '"' | '\'' => UnknownPrefix,
|
||||
c if !c.is_ascii() && c.is_emoji_char() => self.fake_ident_or_unknown_prefix(),
|
||||
c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(),
|
||||
_ => Ident,
|
||||
}
|
||||
}
|
||||
|
||||
fn fake_ident_or_unknown_prefix(&mut self) -> TokenKind {
|
||||
fn invalid_ident(&mut self) -> TokenKind {
|
||||
// Start is already eaten, eat the rest of identifier.
|
||||
self.eat_while(|c| {
|
||||
unicode_xid::UnicodeXID::is_xid_continue(c)
|
||||
|| (!c.is_ascii() && c.is_emoji_char())
|
||||
|| c == '\u{200d}'
|
||||
const ZERO_WIDTH_JOINER: char = '\u{200d}';
|
||||
is_id_continue(c) || (!c.is_ascii() && c.is_emoji_char()) || c == ZERO_WIDTH_JOINER
|
||||
});
|
||||
// Known prefixes must have been handled earlier. So if
|
||||
// we see a prefix here, it is definitely an unknown prefix.
|
||||
match self.first() {
|
||||
'#' | '"' | '\'' => InvalidPrefix,
|
||||
_ => InvalidIdent,
|
||||
}
|
||||
// An invalid identifier followed by '#' or '"' or '\'' could be
|
||||
// interpreted as an invalid literal prefix. We don't bother doing that
|
||||
// because the treatment of invalid identifiers and invalid prefixes
|
||||
// would be the same.
|
||||
InvalidIdent
|
||||
}
|
||||
|
||||
fn c_or_byte_string(
|
||||
|
@ -772,9 +772,6 @@ lint_suspicious_double_ref_clone =
|
||||
lint_suspicious_double_ref_deref =
|
||||
using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type
|
||||
|
||||
lint_tail_expr_drop_order = these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021
|
||||
.label = these values have significant drop implementation and will observe changes in drop order under Edition 2024
|
||||
|
||||
lint_trailing_semi_macro = trailing semicolon in macro used in expression position
|
||||
.note1 = macro invocations at the end of a block are treated as expressions
|
||||
.note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}`
|
||||
|
@ -32,7 +32,7 @@ use rustc_middle::bug;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Upcast, VariantDef};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
|
||||
use rustc_session::lint::FutureIncompatibilityReason;
|
||||
// hardwired lints from rustc_lint_defs
|
||||
pub use rustc_session::lint::builtin::*;
|
||||
@ -586,10 +586,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ty.is_copy_modulo_regions(cx.tcx, cx.param_env) {
|
||||
if ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) {
|
||||
return;
|
||||
}
|
||||
if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.param_env) {
|
||||
if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.typing_env()) {
|
||||
return;
|
||||
}
|
||||
if def.is_variant_list_non_exhaustive()
|
||||
@ -637,8 +637,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||
fn type_implements_negative_copy_modulo_regions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> bool {
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let trait_ref = ty::TraitRef::new(tcx, tcx.require_lang_item(hir::LangItem::Copy, None), [ty]);
|
||||
let pred = ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative };
|
||||
let obligation = traits::Obligation {
|
||||
@ -647,10 +648,7 @@ fn type_implements_negative_copy_modulo_regions<'tcx>(
|
||||
recursion_depth: 0,
|
||||
predicate: pred.upcast(tcx),
|
||||
};
|
||||
|
||||
tcx.infer_ctxt()
|
||||
.build(TypingMode::non_body_analysis())
|
||||
.predicate_must_hold_modulo_regions(&obligation)
|
||||
infcx.predicate_must_hold_modulo_regions(&obligation)
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
@ -43,13 +43,10 @@ declare_lint! {
|
||||
}
|
||||
|
||||
/// FIXME: false negatives (i.e. the lint is not emitted when it should be)
|
||||
/// 1. Method calls that are not checked for:
|
||||
/// - [`temporary_unsafe_cell.get()`][`core::cell::UnsafeCell::get()`]
|
||||
/// - [`temporary_sync_unsafe_cell.get()`][`core::cell::SyncUnsafeCell::get()`]
|
||||
/// 2. Ways to get a temporary that are not recognized:
|
||||
/// 1. Ways to get a temporary that are not recognized:
|
||||
/// - `owning_temporary.field`
|
||||
/// - `owning_temporary[index]`
|
||||
/// 3. No checks for ref-to-ptr conversions:
|
||||
/// 2. No checks for ref-to-ptr conversions:
|
||||
/// - `&raw [mut] temporary`
|
||||
/// - `&temporary as *(const|mut) _`
|
||||
/// - `ptr::from_ref(&temporary)` and friends
|
||||
@ -133,10 +130,11 @@ impl DanglingPointerSearcher<'_, '_> {
|
||||
|
||||
fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind
|
||||
&& matches!(method.ident.name, sym::as_ptr | sym::as_mut_ptr)
|
||||
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& cx.tcx.has_attr(fn_id, sym::rustc_as_ptr)
|
||||
&& is_temporary_rvalue(receiver)
|
||||
&& let ty = cx.typeck_results().expr_ty(receiver)
|
||||
&& is_interesting(cx.tcx, ty)
|
||||
&& owns_allocation(cx.tcx, ty)
|
||||
{
|
||||
// FIXME: use `emit_node_lint` when `#[primary_span]` is added.
|
||||
cx.tcx.emit_node_span_lint(
|
||||
@ -199,24 +197,25 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>,
|
||||
// or any of the above in arbitrary many nested Box'es.
|
||||
fn is_interesting(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
|
||||
// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>, UnsafeCell,
|
||||
// SyncUnsafeCell, or any of the above in arbitrary many nested Box'es.
|
||||
fn owns_allocation(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
|
||||
if ty.is_array() {
|
||||
true
|
||||
} else if let Some(inner) = ty.boxed_ty() {
|
||||
inner.is_slice()
|
||||
|| inner.is_str()
|
||||
|| inner.ty_adt_def().is_some_and(|def| tcx.is_lang_item(def.did(), LangItem::CStr))
|
||||
|| is_interesting(tcx, inner)
|
||||
|| owns_allocation(tcx, inner)
|
||||
} else if let Some(def) = ty.ty_adt_def() {
|
||||
for lang_item in [LangItem::String, LangItem::MaybeUninit] {
|
||||
for lang_item in [LangItem::String, LangItem::MaybeUninit, LangItem::UnsafeCell] {
|
||||
if tcx.is_lang_item(def.did(), lang_item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
tcx.get_diagnostic_name(def.did())
|
||||
.is_some_and(|name| matches!(name, sym::cstring_type | sym::Vec | sym::Cell))
|
||||
tcx.get_diagnostic_name(def.did()).is_some_and(|name| {
|
||||
matches!(name, sym::cstring_type | sym::Vec | sym::Cell | sym::SyncUnsafeCell)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
|
||||
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
|
||||
{
|
||||
let arg_ty = cx.typeck_results().expr_ty(arg);
|
||||
let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
|
||||
let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env());
|
||||
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
|
||||
let let_underscore_ignore_sugg = || {
|
||||
if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0)
|
||||
|
@ -15,7 +15,7 @@ use rustc_middle::ty::relate::{
|
||||
Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
|
||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint::FutureIncompatibilityReason;
|
||||
@ -186,8 +186,8 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
|
||||
functional_variances.variances
|
||||
}),
|
||||
outlives_env: LazyCell::new(|| {
|
||||
let param_env = tcx.param_env(parent_def_id);
|
||||
let infcx = tcx.infer_ctxt().build(TypingMode::from_param_env(param_env));
|
||||
let typing_env = ty::TypingEnv::non_body_analysis(tcx, parent_def_id);
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default();
|
||||
let implied_bounds =
|
||||
|
@ -123,17 +123,19 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
|
||||
let dont_need_to_run: FxIndexSet<LintId> = store
|
||||
.get_lints()
|
||||
.into_iter()
|
||||
.filter(|lint| {
|
||||
// Lints that show up in future-compat reports must always be run.
|
||||
let has_future_breakage =
|
||||
lint.future_incompatible.is_some_and(|fut| fut.reason.has_future_breakage());
|
||||
!has_future_breakage && !lint.eval_always
|
||||
})
|
||||
.filter_map(|lint| {
|
||||
if !lint.eval_always {
|
||||
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
|
||||
if matches!(lint_level, (Level::Allow, ..))
|
||||
|| (matches!(lint_level, (.., LintLevelSource::Default)))
|
||||
&& lint.default_level(tcx.sess.edition()) == Level::Allow
|
||||
{
|
||||
Some(LintId::of(lint))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
|
||||
if matches!(lint_level, (Level::Allow, ..))
|
||||
|| (matches!(lint_level, (.., LintLevelSource::Default)))
|
||||
&& lint.default_level(tcx.sess.edition()) == Level::Allow
|
||||
{
|
||||
Some(LintId::of(lint))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -75,7 +75,6 @@ mod redundant_semicolon;
|
||||
mod reference_casting;
|
||||
mod shadowed_into_iter;
|
||||
mod static_mut_refs;
|
||||
mod tail_expr_drop_order;
|
||||
mod traits;
|
||||
mod types;
|
||||
mod unit_bindings;
|
||||
@ -116,7 +115,6 @@ use rustc_middle::ty::TyCtxt;
|
||||
use shadowed_into_iter::ShadowedIntoIter;
|
||||
pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
|
||||
use static_mut_refs::*;
|
||||
use tail_expr_drop_order::TailExprDropOrder;
|
||||
use traits::*;
|
||||
use types::*;
|
||||
use unit_bindings::*;
|
||||
@ -240,7 +238,6 @@ late_lint_methods!(
|
||||
AsyncFnInTrait: AsyncFnInTrait,
|
||||
NonLocalDefinitions: NonLocalDefinitions::default(),
|
||||
ImplTraitOvercaptures: ImplTraitOvercaptures,
|
||||
TailExprDropOrder: TailExprDropOrder,
|
||||
IfLetRescope: IfLetRescope::default(),
|
||||
StaticMutRefs: StaticMutRefs,
|
||||
UnqualifiedLocalImports: UnqualifiedLocalImports,
|
||||
|
@ -168,7 +168,7 @@ fn is_cast_from_ref_to_mut_ptr<'tcx>(
|
||||
// Except on the presence of non concrete skeleton types (ie generics)
|
||||
// since there is no way to make it safe for arbitrary types.
|
||||
let inner_ty_has_interior_mutability =
|
||||
!inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
|
||||
!inner_ty.is_freeze(cx.tcx, cx.typing_env()) && inner_ty.has_concrete_skeleton();
|
||||
(!need_check_freeze || !inner_ty_has_interior_mutability)
|
||||
.then_some(inner_ty_has_interior_mutability)
|
||||
} else {
|
||||
|
@ -1,308 +0,0 @@
|
||||
use std::mem::swap;
|
||||
|
||||
use rustc_ast::UnOp;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{self as hir, Block, Expr, ExprKind, LetStmt, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::lint::FutureIncompatibilityReason;
|
||||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::edition::Edition;
|
||||
|
||||
use crate::{LateContext, LateLintPass};
|
||||
|
||||
declare_lint! {
|
||||
/// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location,
|
||||
/// that runs a custom `Drop` destructor.
|
||||
/// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior.
|
||||
/// This lint detects those cases and provides you information on those values and their custom destructor implementations.
|
||||
/// Your discretion on this information is required.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,edition2021
|
||||
/// #![warn(tail_expr_drop_order)]
|
||||
/// struct Droppy(i32);
|
||||
/// impl Droppy {
|
||||
/// fn get(&self) -> i32 {
|
||||
/// self.0
|
||||
/// }
|
||||
/// }
|
||||
/// impl Drop for Droppy {
|
||||
/// fn drop(&mut self) {
|
||||
/// // This is a custom destructor and it induces side-effects that is observable
|
||||
/// // especially when the drop order at a tail expression changes.
|
||||
/// println!("loud drop {}", self.0);
|
||||
/// }
|
||||
/// }
|
||||
/// fn edition_2021() -> i32 {
|
||||
/// let another_droppy = Droppy(0);
|
||||
/// Droppy(1).get()
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// edition_2021();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// In tail expression of blocks or function bodies,
|
||||
/// values of type with significant `Drop` implementation has an ill-specified drop order
|
||||
/// before Edition 2024 so that they are dropped only after dropping local variables.
|
||||
/// Edition 2024 introduces a new rule with drop orders for them,
|
||||
/// so that they are dropped first before dropping local variables.
|
||||
///
|
||||
/// A significant `Drop::drop` destructor here refers to an explicit, arbitrary
|
||||
/// implementation of the `Drop` trait on the type, with exceptions including `Vec`,
|
||||
/// `Box`, `Rc`, `BTreeMap` and `HashMap` that are marked by the compiler otherwise
|
||||
/// so long that the generic types have no significant destructor recursively.
|
||||
/// In other words, a type has a significant drop destructor when it has a `Drop` implementation
|
||||
/// or its destructor invokes a significant destructor on a type.
|
||||
/// Since we cannot completely reason about the change by just inspecting the existence of
|
||||
/// a significant destructor, this lint remains only a suggestion and is set to `allow` by default.
|
||||
///
|
||||
/// This lint only points out the issue with `Droppy`, which will be dropped before `another_droppy`
|
||||
/// does in Edition 2024.
|
||||
/// No fix will be proposed by this lint.
|
||||
/// However, the most probable fix is to hoist `Droppy` into its own local variable binding.
|
||||
/// ```rust
|
||||
/// struct Droppy(i32);
|
||||
/// impl Droppy {
|
||||
/// fn get(&self) -> i32 {
|
||||
/// self.0
|
||||
/// }
|
||||
/// }
|
||||
/// fn edition_2024() -> i32 {
|
||||
/// let value = Droppy(0);
|
||||
/// let another_droppy = Droppy(1);
|
||||
/// value.get()
|
||||
/// }
|
||||
/// ```
|
||||
pub TAIL_EXPR_DROP_ORDER,
|
||||
Allow,
|
||||
"Detect and warn on significant change in drop order in tail expression location",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "issue #123739 <https://github.com/rust-lang/rust/issues/123739>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint_pass!(TailExprDropOrder => [TAIL_EXPR_DROP_ORDER]);
|
||||
|
||||
impl TailExprDropOrder {
|
||||
fn check_fn_or_closure<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
fn_kind: hir::intravisit::FnKind<'tcx>,
|
||||
body: &'tcx hir::Body<'tcx>,
|
||||
def_id: rustc_span::def_id::LocalDefId,
|
||||
) {
|
||||
let mut locals = vec![];
|
||||
if matches!(fn_kind, hir::intravisit::FnKind::Closure) {
|
||||
for &capture in cx.tcx.closure_captures(def_id) {
|
||||
if matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue)
|
||||
&& capture.place.ty().has_significant_drop(cx.tcx, cx.typing_env())
|
||||
{
|
||||
locals.push(capture.var_ident.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
for param in body.params {
|
||||
if cx
|
||||
.typeck_results()
|
||||
.node_type(param.hir_id)
|
||||
.has_significant_drop(cx.tcx, cx.typing_env())
|
||||
{
|
||||
locals.push(param.span);
|
||||
}
|
||||
}
|
||||
if let hir::ExprKind::Block(block, _) = body.value.kind {
|
||||
LintVisitor { cx, locals }.check_block_inner(block);
|
||||
} else {
|
||||
LintTailExpr { cx, locals: &locals, is_root_tail_expr: true }.visit_expr(body.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
fn_kind: hir::intravisit::FnKind<'tcx>,
|
||||
_: &'tcx hir::FnDecl<'tcx>,
|
||||
body: &'tcx hir::Body<'tcx>,
|
||||
_: Span,
|
||||
def_id: rustc_span::def_id::LocalDefId,
|
||||
) {
|
||||
if !body.value.span.edition().at_least_rust_2024() {
|
||||
Self::check_fn_or_closure(cx, fn_kind, body, def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LintVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
// We only record locals that have significant drops
|
||||
locals: Vec<Span>,
|
||||
}
|
||||
|
||||
struct LocalCollector<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
locals: &'a mut Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LocalCollector<'a, 'tcx> {
|
||||
type Result = ();
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
|
||||
if let PatKind::Binding(_binding_mode, id, ident, pat) = pat.kind {
|
||||
let ty = self.cx.typeck_results().node_type(id);
|
||||
if ty.has_significant_drop(self.cx.tcx, self.cx.typing_env()) {
|
||||
self.locals.push(ident.span);
|
||||
}
|
||||
if let Some(pat) = pat {
|
||||
self.visit_pat(pat);
|
||||
}
|
||||
} else {
|
||||
intravisit::walk_pat(self, pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LintVisitor<'a, 'tcx> {
|
||||
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
|
||||
let mut locals = <_>::default();
|
||||
swap(&mut locals, &mut self.locals);
|
||||
self.check_block_inner(block);
|
||||
swap(&mut locals, &mut self.locals);
|
||||
}
|
||||
fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) {
|
||||
LocalCollector { cx: self.cx, locals: &mut self.locals }.visit_local(local);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LintVisitor<'a, 'tcx> {
|
||||
fn check_block_inner(&mut self, block: &Block<'tcx>) {
|
||||
if block.span.at_least_rust_2024() {
|
||||
// We only lint up to Edition 2021
|
||||
return;
|
||||
}
|
||||
let Some(tail_expr) = block.expr else { return };
|
||||
for stmt in block.stmts {
|
||||
match stmt.kind {
|
||||
StmtKind::Let(let_stmt) => self.visit_local(let_stmt),
|
||||
StmtKind::Item(_) => {}
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e),
|
||||
}
|
||||
}
|
||||
if self.locals.is_empty() {
|
||||
return;
|
||||
}
|
||||
LintTailExpr { cx: self.cx, locals: &self.locals, is_root_tail_expr: true }
|
||||
.visit_expr(tail_expr);
|
||||
}
|
||||
}
|
||||
|
||||
struct LintTailExpr<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
is_root_tail_expr: bool,
|
||||
locals: &'a [Span],
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LintTailExpr<'a, 'tcx> {
|
||||
fn expr_eventually_point_into_local(mut expr: &Expr<'tcx>) -> bool {
|
||||
loop {
|
||||
match expr.kind {
|
||||
ExprKind::Index(access, _, _) | ExprKind::Field(access, _) => expr = access,
|
||||
ExprKind::AddrOf(_, _, referee) | ExprKind::Unary(UnOp::Deref, referee) => {
|
||||
expr = referee
|
||||
}
|
||||
ExprKind::Path(_)
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind
|
||||
&& let [local, ..] = path.segments
|
||||
&& let Res::Local(_) = local.res =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_generates_nonlocal_droppy_value(&self, expr: &Expr<'tcx>) -> bool {
|
||||
if Self::expr_eventually_point_into_local(expr) {
|
||||
return false;
|
||||
}
|
||||
self.cx
|
||||
.typeck_results()
|
||||
.expr_ty(expr)
|
||||
.has_significant_drop(self.cx.tcx, self.cx.typing_env())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LintTailExpr<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if self.is_root_tail_expr {
|
||||
self.is_root_tail_expr = false;
|
||||
} else if self.expr_generates_nonlocal_droppy_value(expr) {
|
||||
self.cx.tcx.emit_node_span_lint(
|
||||
TAIL_EXPR_DROP_ORDER,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
TailExprDropOrderLint { spans: self.locals.to_vec() },
|
||||
);
|
||||
return;
|
||||
}
|
||||
match expr.kind {
|
||||
ExprKind::Match(scrutinee, _, _) => self.visit_expr(scrutinee),
|
||||
|
||||
ExprKind::ConstBlock(_)
|
||||
| ExprKind::Array(_)
|
||||
| ExprKind::Break(_, _)
|
||||
| ExprKind::Continue(_)
|
||||
| ExprKind::Ret(_)
|
||||
| ExprKind::Become(_)
|
||||
| ExprKind::Yield(_, _)
|
||||
| ExprKind::InlineAsm(_)
|
||||
| ExprKind::If(_, _, _)
|
||||
| ExprKind::Loop(_, _, _, _)
|
||||
| ExprKind::Closure(_)
|
||||
| ExprKind::DropTemps(_)
|
||||
| ExprKind::OffsetOf(_, _)
|
||||
| ExprKind::Assign(_, _, _)
|
||||
| ExprKind::AssignOp(_, _, _)
|
||||
| ExprKind::Lit(_)
|
||||
| ExprKind::Err(_) => {}
|
||||
|
||||
ExprKind::MethodCall(_, _, _, _)
|
||||
| ExprKind::Call(_, _)
|
||||
| ExprKind::Type(_, _)
|
||||
| ExprKind::Tup(_)
|
||||
| ExprKind::Binary(_, _, _)
|
||||
| ExprKind::Unary(_, _)
|
||||
| ExprKind::Path(_)
|
||||
| ExprKind::Let(_)
|
||||
| ExprKind::Cast(_, _)
|
||||
| ExprKind::Field(_, _)
|
||||
| ExprKind::Index(_, _, _)
|
||||
| ExprKind::AddrOf(_, _, _)
|
||||
| ExprKind::Struct(_, _, _)
|
||||
| ExprKind::Repeat(_, _) => intravisit::walk_expr(self, expr),
|
||||
|
||||
ExprKind::Block(_, _) => {
|
||||
// We do not lint further because the drop order stays the same inside the block
|
||||
}
|
||||
}
|
||||
}
|
||||
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
|
||||
LintVisitor { cx: self.cx, locals: <_>::default() }.check_block_inner(block);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_tail_expr_drop_order)]
|
||||
struct TailExprDropOrderLint {
|
||||
#[label]
|
||||
pub spans: Vec<Span>,
|
||||
}
|
@ -292,7 +292,7 @@ fn lint_wide_pointer<'tcx>(
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
(!ty.is_sized(cx.tcx, cx.param_env))
|
||||
(!ty.is_sized(cx.tcx, cx.typing_env()))
|
||||
.then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))))
|
||||
};
|
||||
|
||||
@ -904,7 +904,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
if let Some(boxed) = ty.boxed_ty()
|
||||
&& matches!(self.mode, CItemKind::Definition)
|
||||
{
|
||||
if boxed.is_sized(tcx, self.cx.param_env) {
|
||||
if boxed.is_sized(tcx, self.cx.typing_env()) {
|
||||
return FfiSafe;
|
||||
} else {
|
||||
return FfiUnsafe {
|
||||
@ -1069,7 +1069,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
|
||||
if {
|
||||
matches!(self.mode, CItemKind::Definition)
|
||||
&& ty.is_sized(self.cx.tcx, self.cx.param_env)
|
||||
&& ty.is_sized(self.cx.tcx, self.cx.typing_env())
|
||||
} =>
|
||||
{
|
||||
FfiSafe
|
||||
|
@ -101,6 +101,7 @@ declare_lint_pass! {
|
||||
SINGLE_USE_LIFETIMES,
|
||||
SOFT_UNSTABLE,
|
||||
STABLE_FEATURES,
|
||||
TAIL_EXPR_DROP_ORDER,
|
||||
TEST_UNSTABLE_LINT,
|
||||
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
|
||||
TRIVIAL_CASTS,
|
||||
@ -4994,6 +4995,83 @@ declare_lint! {
|
||||
"detects pointer to integer transmutes in const functions and associated constants",
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location,
|
||||
/// that runs a custom `Drop` destructor.
|
||||
/// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior.
|
||||
/// This lint detects those cases and provides you information on those values and their custom destructor implementations.
|
||||
/// Your discretion on this information is required.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,edition2021
|
||||
/// #![warn(tail_expr_drop_order)]
|
||||
/// struct Droppy(i32);
|
||||
/// impl Droppy {
|
||||
/// fn get(&self) -> i32 {
|
||||
/// self.0
|
||||
/// }
|
||||
/// }
|
||||
/// impl Drop for Droppy {
|
||||
/// fn drop(&mut self) {
|
||||
/// // This is a custom destructor and it induces side-effects that is observable
|
||||
/// // especially when the drop order at a tail expression changes.
|
||||
/// println!("loud drop {}", self.0);
|
||||
/// }
|
||||
/// }
|
||||
/// fn edition_2021() -> i32 {
|
||||
/// let another_droppy = Droppy(0);
|
||||
/// Droppy(1).get()
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// edition_2021();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// In tail expression of blocks or function bodies,
|
||||
/// values of type with significant `Drop` implementation has an ill-specified drop order
|
||||
/// before Edition 2024 so that they are dropped only after dropping local variables.
|
||||
/// Edition 2024 introduces a new rule with drop orders for them,
|
||||
/// so that they are dropped first before dropping local variables.
|
||||
///
|
||||
/// A significant `Drop::drop` destructor here refers to an explicit, arbitrary
|
||||
/// implementation of the `Drop` trait on the type, with exceptions including `Vec`,
|
||||
/// `Box`, `Rc`, `BTreeMap` and `HashMap` that are marked by the compiler otherwise
|
||||
/// so long that the generic types have no significant destructor recursively.
|
||||
/// In other words, a type has a significant drop destructor when it has a `Drop` implementation
|
||||
/// or its destructor invokes a significant destructor on a type.
|
||||
/// Since we cannot completely reason about the change by just inspecting the existence of
|
||||
/// a significant destructor, this lint remains only a suggestion and is set to `allow` by default.
|
||||
///
|
||||
/// This lint only points out the issue with `Droppy`, which will be dropped before `another_droppy`
|
||||
/// does in Edition 2024.
|
||||
/// No fix will be proposed by this lint.
|
||||
/// However, the most probable fix is to hoist `Droppy` into its own local variable binding.
|
||||
/// ```rust
|
||||
/// struct Droppy(i32);
|
||||
/// impl Droppy {
|
||||
/// fn get(&self) -> i32 {
|
||||
/// self.0
|
||||
/// }
|
||||
/// }
|
||||
/// fn edition_2024() -> i32 {
|
||||
/// let value = Droppy(0);
|
||||
/// let another_droppy = Droppy(1);
|
||||
/// value.get()
|
||||
/// }
|
||||
/// ```
|
||||
pub TAIL_EXPR_DROP_ORDER,
|
||||
Allow,
|
||||
"Detect and warn on significant change in drop order in tail expression location",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "issue #123739 <https://github.com/rust-lang/rust/issues/123739>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `rust_2024_guarded_string_incompatible_syntax` lint detects `#` tokens
|
||||
/// that will be parsed as part of a guarded string literal in Rust 2024.
|
||||
|
@ -466,6 +466,20 @@ impl FutureIncompatibilityReason {
|
||||
| FutureIncompatibilityReason::Custom(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_future_breakage(self) -> bool {
|
||||
match self {
|
||||
FutureIncompatibilityReason::FutureReleaseErrorReportInDeps => true,
|
||||
|
||||
FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps
|
||||
| FutureIncompatibilityReason::FutureReleaseSemanticsChange
|
||||
| FutureIncompatibilityReason::EditionError(_)
|
||||
| FutureIncompatibilityReason::EditionSemanticsChange(_)
|
||||
| FutureIncompatibilityReason::EditionAndFutureReleaseError(_)
|
||||
| FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(_)
|
||||
| FutureIncompatibilityReason::Custom(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FutureIncompatibleInfo {
|
||||
|
@ -290,12 +290,7 @@ pub fn lint_level(
|
||||
let has_future_breakage = future_incompatible.map_or(
|
||||
// Default allow lints trigger too often for testing.
|
||||
sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
|
||||
|incompat| {
|
||||
matches!(
|
||||
incompat.reason,
|
||||
FutureIncompatibilityReason::FutureReleaseErrorReportInDeps
|
||||
)
|
||||
},
|
||||
|incompat| incompat.reason.has_future_breakage(),
|
||||
);
|
||||
|
||||
// Convert lint level to error level.
|
||||
|
@ -236,6 +236,11 @@ pub struct ScopeTree {
|
||||
/// during type check based on a traversal of the AST.
|
||||
pub rvalue_candidates: HirIdMap<RvalueCandidateType>,
|
||||
|
||||
/// Backwards incompatible scoping that will be introduced in future editions.
|
||||
/// This information is used later for linting to identify locals and
|
||||
/// temporary values that will receive backwards-incompatible drop orders.
|
||||
pub backwards_incompatible_scope: UnordMap<hir::ItemLocalId, Scope>,
|
||||
|
||||
/// If there are any `yield` nested within a scope, this map
|
||||
/// stores the `Span` of the last one and its index in the
|
||||
/// postorder of the Visitor traversal on the HIR.
|
||||
|
@ -46,7 +46,7 @@ pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
|
||||
pub use self::value::Scalar;
|
||||
use crate::mir;
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
use crate::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
||||
/// Uniquely identifies one of the following:
|
||||
/// - A constant
|
||||
@ -312,7 +312,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutability(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Mutability {
|
||||
pub fn mutability(&self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Mutability {
|
||||
// Let's see what kind of memory we are.
|
||||
match self {
|
||||
GlobalAlloc::Static(did) => {
|
||||
@ -334,7 +334,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
|
||||
.type_of(did)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters")
|
||||
.is_freeze(tcx, param_env) =>
|
||||
.is_freeze(tcx, typing_env) =>
|
||||
{
|
||||
Mutability::Mut
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let args = GenericArgs::identity_for_item(self, def_id);
|
||||
let instance = ty::Instance::new(def_id, args);
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
let param_env = self.param_env(def_id).with_reveal_all_normalized(self);
|
||||
self.const_eval_global_id(param_env, cid, DUMMY_SP)
|
||||
let typing_env = ty::TypingEnv::post_analysis(self, def_id);
|
||||
self.const_eval_global_id(typing_env, cid, DUMMY_SP)
|
||||
}
|
||||
|
||||
/// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts
|
||||
@ -41,8 +41,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let args = GenericArgs::identity_for_item(self, def_id);
|
||||
let instance = ty::Instance::new(def_id, args);
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
let param_env = self.param_env(def_id).with_reveal_all_normalized(self);
|
||||
let inputs = self.erase_regions(param_env.and(cid));
|
||||
let typing_env = ty::TypingEnv::post_analysis(self, def_id);
|
||||
let inputs = self.erase_regions(typing_env.as_query_input(cid));
|
||||
self.eval_to_allocation_raw(inputs)
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) {
|
||||
Ok(Some(instance)) => {
|
||||
let cid = GlobalId { instance, promoted: ct.promoted };
|
||||
self.const_eval_global_id(typing_env.param_env, cid, span)
|
||||
self.const_eval_global_id(typing_env, cid, span)
|
||||
}
|
||||
// For errors during resolution, we deliberately do not point at the usage site of the constant,
|
||||
// since for these errors the place the constant is used shouldn't matter.
|
||||
@ -105,7 +105,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) {
|
||||
Ok(Some(instance)) => {
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
self.const_eval_global_id_for_typeck(typing_env.param_env, cid, span).inspect(|_| {
|
||||
self.const_eval_global_id_for_typeck(typing_env, cid, span).inspect(|_| {
|
||||
// We are emitting the lint here instead of in `is_const_evaluatable`
|
||||
// as we normalize obligations before checking them, and normalization
|
||||
// uses this function to evaluate this constant.
|
||||
@ -144,24 +144,25 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
|
||||
pub fn const_eval_instance(
|
||||
self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
span: Span,
|
||||
) -> EvalToConstValueResult<'tcx> {
|
||||
self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span)
|
||||
self.const_eval_global_id(typing_env, GlobalId { instance, promoted: None }, span)
|
||||
}
|
||||
|
||||
/// Evaluate a constant to a `ConstValue`.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn const_eval_global_id(
|
||||
self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
span: Span,
|
||||
) -> EvalToConstValueResult<'tcx> {
|
||||
// Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
|
||||
// improve caching of queries.
|
||||
let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid));
|
||||
let inputs =
|
||||
self.erase_regions(typing_env.with_reveal_all_normalized(self).as_query_input(cid));
|
||||
if !span.is_dummy() {
|
||||
// The query doesn't know where it is being invoked, so we need to fix the span.
|
||||
self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span))
|
||||
@ -174,13 +175,14 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn const_eval_global_id_for_typeck(
|
||||
self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
span: Span,
|
||||
) -> EvalToValTreeResult<'tcx> {
|
||||
// Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
|
||||
// improve caching of queries.
|
||||
let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid));
|
||||
let inputs =
|
||||
self.erase_regions(typing_env.with_reveal_all_normalized(self).as_query_input(cid));
|
||||
debug!(?inputs);
|
||||
if !span.is_dummy() {
|
||||
// The query doesn't know where it is being invoked, so we need to fix the span.
|
||||
@ -202,12 +204,12 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
|
||||
// into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are
|
||||
// encountered.
|
||||
let args = GenericArgs::identity_for_item(self.tcx, def_id);
|
||||
let instance = ty::Instance::new(def_id, args);
|
||||
let instance = ty::Instance::new(def_id, self.tcx.erase_regions(args));
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
let param_env = self.tcx.param_env(def_id).with_reveal_all_normalized(self.tcx);
|
||||
let typing_env = ty::TypingEnv::post_analysis(self.tcx, def_id);
|
||||
// Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
|
||||
// improve caching of queries.
|
||||
let inputs = self.tcx.erase_regions(param_env.and(cid));
|
||||
let inputs = self.tcx.erase_regions(typing_env.as_query_input(cid));
|
||||
self.eval_to_const_value_raw(inputs)
|
||||
}
|
||||
}
|
||||
|
@ -459,10 +459,7 @@ impl<'tcx> Body<'tcx> {
|
||||
typing_mode: ty::TypingMode::non_body_analysis(),
|
||||
param_env: tcx.param_env(self.source.def_id()),
|
||||
},
|
||||
MirPhase::Runtime(_) => TypingEnv {
|
||||
typing_mode: ty::TypingMode::PostAnalysis,
|
||||
param_env: tcx.param_env_reveal_all_normalized(self.source.def_id()),
|
||||
},
|
||||
MirPhase::Runtime(_) => TypingEnv::post_analysis(tcx, self.source.def_id()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,6 +834,11 @@ impl Debug for Statement<'_> {
|
||||
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
|
||||
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
|
||||
Nop => write!(fmt, "nop"),
|
||||
BackwardIncompatibleDropHint { ref place, reason: _ } => {
|
||||
// For now, we don't record the reason because there is only one use case,
|
||||
// which is to report breaking change in drop order by Edition 2024
|
||||
write!(fmt, "backward incompatible drop({place:?})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -432,6 +432,18 @@ pub enum StatementKind<'tcx> {
|
||||
|
||||
/// No-op. Useful for deleting instructions without affecting statement indices.
|
||||
Nop,
|
||||
|
||||
/// Marker statement indicating where `place` would be dropped.
|
||||
/// This is semantically equivalent to `Nop`, so codegen and MIRI should interpret this
|
||||
/// statement as such.
|
||||
/// The only use case of this statement is for linting in MIR to detect temporary lifetime
|
||||
/// changes.
|
||||
BackwardIncompatibleDropHint {
|
||||
/// Place to drop
|
||||
place: Box<Place<'tcx>>,
|
||||
/// Reason for backward incompatibility
|
||||
reason: BackwardIncompatibleDropReason,
|
||||
},
|
||||
}
|
||||
|
||||
impl StatementKind<'_> {
|
||||
@ -452,6 +464,7 @@ impl StatementKind<'_> {
|
||||
StatementKind::Intrinsic(..) => "Intrinsic",
|
||||
StatementKind::ConstEvalCounter => "ConstEvalCounter",
|
||||
StatementKind::Nop => "Nop",
|
||||
StatementKind::BackwardIncompatibleDropHint { .. } => "BackwardIncompatibleDropHint",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -897,6 +910,21 @@ pub enum TerminatorKind<'tcx> {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
TyEncodable,
|
||||
TyDecodable,
|
||||
Hash,
|
||||
HashStable,
|
||||
PartialEq,
|
||||
TypeFoldable,
|
||||
TypeVisitable
|
||||
)]
|
||||
pub enum BackwardIncompatibleDropReason {
|
||||
Edition2024,
|
||||
}
|
||||
|
||||
impl TerminatorKind<'_> {
|
||||
/// Returns a simple string representation of a `TerminatorKind` variant, independent of any
|
||||
/// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
|
||||
|
@ -452,6 +452,7 @@ macro_rules! make_mir_visitor {
|
||||
}
|
||||
StatementKind::ConstEvalCounter => {}
|
||||
StatementKind::Nop => {}
|
||||
StatementKind::BackwardIncompatibleDropHint { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,6 +360,14 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Key for ty::ParamEnvAnd<'tcx, Ty<'tcx>> {
|
||||
type Cache<V> = DefaultCache<Self, V>;
|
||||
|
||||
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
|
||||
DUMMY_SP
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Key for ty::TraitRef<'tcx> {
|
||||
type Cache<V> = DefaultCache<Self, V>;
|
||||
|
||||
@ -456,18 +464,6 @@ impl<'tcx> Key for ty::ParamEnv<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
|
||||
type Cache<V> = DefaultCache<Self, V>;
|
||||
|
||||
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
|
||||
self.value.default_span(tcx)
|
||||
}
|
||||
|
||||
fn ty_def_id(&self) -> Option<DefId> {
|
||||
self.value.ty_def_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> {
|
||||
type Cache<V> = DefaultCache<Self, V>;
|
||||
|
||||
|
@ -1095,7 +1095,7 @@ rustc_queries! {
|
||||
/// Evaluates a constant and returns the computed allocation.
|
||||
///
|
||||
/// **Do not use this** directly, use the `eval_to_const_value` or `eval_to_valtree` instead.
|
||||
query eval_to_allocation_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
|
||||
query eval_to_allocation_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>)
|
||||
-> EvalToAllocationRawResult<'tcx> {
|
||||
desc { |tcx|
|
||||
"const-evaluating + checking `{}`",
|
||||
@ -1121,7 +1121,7 @@ rustc_queries! {
|
||||
///
|
||||
/// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`,
|
||||
/// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`.
|
||||
query eval_to_const_value_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
|
||||
query eval_to_const_value_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>)
|
||||
-> EvalToConstValueResult<'tcx> {
|
||||
desc { |tcx|
|
||||
"simplifying constant for the type system `{}`",
|
||||
@ -1133,7 +1133,7 @@ rustc_queries! {
|
||||
/// Evaluate a constant and convert it to a type level constant or
|
||||
/// return `None` if that is not possible.
|
||||
query eval_to_valtree(
|
||||
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>
|
||||
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>
|
||||
) -> EvalToValTreeResult<'tcx> {
|
||||
desc { "evaluating type-level constant" }
|
||||
}
|
||||
@ -1390,19 +1390,19 @@ rustc_queries! {
|
||||
|
||||
/// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`,
|
||||
/// `ty.is_copy()`, etc, since that will prune the environment where possible.
|
||||
query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
query is_copy_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Copy`", env.value }
|
||||
}
|
||||
/// Query backing `Ty::is_sized`.
|
||||
query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
query is_sized_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Sized`", env.value }
|
||||
}
|
||||
/// Query backing `Ty::is_freeze`.
|
||||
query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is freeze", env.value }
|
||||
}
|
||||
/// Query backing `Ty::is_unpin`.
|
||||
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Unpin`", env.value }
|
||||
}
|
||||
/// Query backing `Ty::needs_drop`.
|
||||
@ -1448,6 +1448,28 @@ rustc_queries! {
|
||||
cache_on_disk_if { false }
|
||||
}
|
||||
|
||||
/// Returns a list of types which (a) have a potentially significant destructor
|
||||
/// and (b) may be dropped as a result of dropping a value of some type `ty`
|
||||
/// (in the given environment).
|
||||
///
|
||||
/// The idea of "significant" drop is somewhat informal and is used only for
|
||||
/// diagnostics and edition migrations. The idea is that a significant drop may have
|
||||
/// some visible side-effect on execution; freeing memory is NOT considered a side-effect.
|
||||
/// The rules are as follows:
|
||||
/// * Type with no explicit drop impl do not have significant drop.
|
||||
/// * Types with a drop impl are assumed to have significant drop unless they have a `#[rustc_insignificant_dtor]` annotation.
|
||||
///
|
||||
/// Note that insignificant drop is a "shallow" property. A type like `Vec<LockGuard>` does not
|
||||
/// have significant drop but the type `LockGuard` does, and so if `ty = Vec<LockGuard>`
|
||||
/// then the return value would be `&[LockGuard]`.
|
||||
/// *IMPORTANT*: *DO NOT* run this query before promoted MIR body is constructed,
|
||||
/// because this query partially depends on that query.
|
||||
/// Otherwise, there is a risk of query cycles.
|
||||
query list_significant_drop_tys(ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx ty::List<Ty<'tcx>> {
|
||||
desc { |tcx| "computing when `{}` has a significant destructor", ty.value }
|
||||
cache_on_disk_if { false }
|
||||
}
|
||||
|
||||
/// Computes the layout of a type. Note that this implicitly
|
||||
/// executes in "reveal all" mode, and will normalize the input type.
|
||||
query layout_of(
|
||||
|
@ -247,13 +247,24 @@ pub struct Expr<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
|
||||
/// The lifetime of this expression if it should be spilled into a
|
||||
/// temporary; should be `None` only if in a constant context
|
||||
pub temp_lifetime: Option<region::Scope>,
|
||||
/// temporary
|
||||
pub temp_lifetime: TempLifetime,
|
||||
|
||||
/// span of the expression in the source
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// Temporary lifetime information for THIR expressions
|
||||
#[derive(Clone, Copy, Debug, HashStable)]
|
||||
pub struct TempLifetime {
|
||||
/// Lifetime for temporaries as expected.
|
||||
/// This should be `None` in a constant context.
|
||||
pub temp_lifetime: Option<region::Scope>,
|
||||
/// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition.
|
||||
/// If `None`, then no changes are expected, or lints are disabled.
|
||||
pub backwards_incompatible: Option<region::Scope>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
pub enum ExprKind<'tcx> {
|
||||
/// `Scope`s are used to explicitly mark destruction scopes,
|
||||
@ -1092,7 +1103,7 @@ mod size_asserts {
|
||||
use super::*;
|
||||
// tidy-alphabetical-start
|
||||
static_assert_size!(Block, 48);
|
||||
static_assert_size!(Expr<'_>, 64);
|
||||
static_assert_size!(Expr<'_>, 72);
|
||||
static_assert_size!(ExprKind<'_>, 40);
|
||||
static_assert_size!(Pat<'_>, 64);
|
||||
static_assert_size!(PatKind<'_>, 48);
|
||||
|
@ -376,7 +376,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
|
||||
}
|
||||
|
||||
fn is_const_impl(self, def_id: DefId) -> bool {
|
||||
fn impl_is_const(self, def_id: DefId) -> bool {
|
||||
self.is_conditionally_const(def_id)
|
||||
}
|
||||
|
||||
fn fn_is_const(self, def_id: DefId) -> bool {
|
||||
self.is_conditionally_const(def_id)
|
||||
}
|
||||
|
||||
@ -598,19 +602,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
self.coroutine_is_async_gen(coroutine_def_id)
|
||||
}
|
||||
|
||||
// We don't use `TypingEnv` here as it's only defined in `rustc_middle` and
|
||||
// `rustc_next_trait_solver` shouldn't have to know about it.
|
||||
fn layout_is_pointer_like(
|
||||
self,
|
||||
typing_mode: ty::TypingMode<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let typing_env = ty::TypingEnv { typing_mode, param_env };
|
||||
self.layout_of(self.erase_regions(typing_env).as_query_input(self.erase_regions(ty)))
|
||||
.is_ok_and(|layout| layout.layout.is_pointer_like(&self.data_layout))
|
||||
}
|
||||
|
||||
type UnsizingParams = &'tcx rustc_index::bit_set::BitSet<u32>;
|
||||
fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams {
|
||||
self.unsizing_params_for_adt(adt_def_id)
|
||||
@ -684,7 +675,6 @@ bidirectional_lang_item_map! {
|
||||
Metadata,
|
||||
Option,
|
||||
PointeeTrait,
|
||||
PointerLike,
|
||||
Poll,
|
||||
Sized,
|
||||
TransmuteTrait,
|
||||
|
@ -537,9 +537,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
|
||||
// All regions in the result of this query are erased, so it's
|
||||
// fine to erase all of the input regions.
|
||||
let typing_env = tcx.erase_regions(typing_env);
|
||||
let args = tcx.erase_regions(args);
|
||||
tcx.resolve_instance_raw(typing_env.as_query_input((def_id, args)))
|
||||
tcx.resolve_instance_raw(tcx.erase_regions(typing_env.as_query_input((def_id, args))))
|
||||
}
|
||||
|
||||
pub fn expect_resolve(
|
||||
|
@ -1002,12 +1002,12 @@ where
|
||||
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
let kind = match mt {
|
||||
hir::Mutability::Not => PointerKind::SharedRef {
|
||||
frozen: optimize && ty.is_freeze(tcx, typing_env.param_env),
|
||||
},
|
||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
||||
unpin: optimize && ty.is_unpin(tcx, typing_env.param_env),
|
||||
},
|
||||
hir::Mutability::Not => {
|
||||
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
|
||||
}
|
||||
hir::Mutability::Mut => {
|
||||
PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) }
|
||||
}
|
||||
};
|
||||
|
||||
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
|
||||
@ -1100,7 +1100,7 @@ where
|
||||
debug_assert!(pointee.safe.is_none());
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
pointee.safe = Some(PointerKind::Box {
|
||||
unpin: optimize && boxed_ty.is_unpin(tcx, typing_env.param_env),
|
||||
unpin: optimize && boxed_ty.is_unpin(tcx, typing_env),
|
||||
global: this.ty.is_box_global(tcx),
|
||||
});
|
||||
}
|
||||
|
@ -1140,12 +1140,6 @@ pub struct TypingEnv<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> TypingEnv<'tcx> {
|
||||
// FIXME(#132279): This method should be removed but simplifies the
|
||||
// transition.
|
||||
pub fn from_param_env(param_env: ParamEnv<'tcx>) -> TypingEnv<'tcx> {
|
||||
TypingEnv { typing_mode: TypingMode::from_param_env(param_env), param_env }
|
||||
}
|
||||
|
||||
/// Create a typing environment with no where-clauses in scope
|
||||
/// where all opaque types and default associated items are revealed.
|
||||
///
|
||||
@ -1192,7 +1186,6 @@ impl<'tcx> TypingEnv<'tcx> {
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
{
|
||||
debug_assert!(!value.has_infer());
|
||||
// FIXME(#132279): We should assert that the value does not contain any placeholders
|
||||
// as these placeholders are also local to the current inference context. However, we
|
||||
// currently use pseudo-canonical queries in the trait solver which replaces params with
|
||||
@ -1215,7 +1208,7 @@ impl<'tcx> TypingEnv<'tcx> {
|
||||
/// This should be created by using `infcx.pseudo_canonicalize_query(param_env, value)`
|
||||
/// or by using `typing_env.as_query_input(value)`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable)]
|
||||
#[derive(HashStable, TypeVisitable, TypeFoldable)]
|
||||
pub struct PseudoCanonicalInput<'tcx, T> {
|
||||
pub typing_env: TypingEnv<'tcx>,
|
||||
pub value: T,
|
||||
|
@ -18,15 +18,17 @@ impl RvalueScopes {
|
||||
}
|
||||
|
||||
/// Returns the scope when the temp created by `expr_id` will be cleaned up.
|
||||
/// It also emits a lint on potential backwards incompatible change to the temporary scope
|
||||
/// which is *for now* always shortening.
|
||||
pub fn temporary_scope(
|
||||
&self,
|
||||
region_scope_tree: &ScopeTree,
|
||||
expr_id: hir::ItemLocalId,
|
||||
) -> Option<Scope> {
|
||||
) -> (Option<Scope>, Option<Scope>) {
|
||||
// Check for a designated rvalue scope.
|
||||
if let Some(&s) = self.map.get(&expr_id) {
|
||||
debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
|
||||
return s;
|
||||
return (s, None);
|
||||
}
|
||||
|
||||
// Otherwise, locate the innermost terminating scope
|
||||
@ -34,27 +36,40 @@ impl RvalueScopes {
|
||||
// have an enclosing scope, hence no scope will be
|
||||
// returned.
|
||||
let mut id = Scope { id: expr_id, data: ScopeData::Node };
|
||||
let mut backwards_incompatible = None;
|
||||
|
||||
while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
|
||||
match p.data {
|
||||
ScopeData::Destruction => {
|
||||
debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
|
||||
return Some(id);
|
||||
return (Some(id), backwards_incompatible);
|
||||
}
|
||||
ScopeData::IfThenRescope => {
|
||||
debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
|
||||
return Some(p);
|
||||
return (Some(p), backwards_incompatible);
|
||||
}
|
||||
ScopeData::Node
|
||||
| ScopeData::CallSite
|
||||
| ScopeData::Arguments
|
||||
| ScopeData::IfThen
|
||||
| ScopeData::Remainder(_) => id = p,
|
||||
| ScopeData::Remainder(_) => {
|
||||
// If we haven't already passed through a backwards-incompatible node,
|
||||
// then check if we are passing through one now and record it if so.
|
||||
// This is for now only working for cases where a temporary lifetime is
|
||||
// *shortened*.
|
||||
if backwards_incompatible.is_none() {
|
||||
backwards_incompatible = region_scope_tree
|
||||
.backwards_incompatible_scope
|
||||
.get(&p.item_local_id())
|
||||
.copied();
|
||||
}
|
||||
id = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("temporary_scope({expr_id:?}) = None");
|
||||
None
|
||||
(None, backwards_incompatible)
|
||||
}
|
||||
|
||||
/// Make an association between a sub-expression and an extended lifetime
|
||||
|
@ -1180,8 +1180,12 @@ impl<'tcx> Ty<'tcx> {
|
||||
/// does copies even when the type actually doesn't satisfy the
|
||||
/// full requirements for the `Copy` trait (cc #29149) -- this
|
||||
/// winds up being reported as an error during NLL borrow check.
|
||||
pub fn is_copy_modulo_regions(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.is_trivially_pure_clone_copy() || tcx.is_copy_raw(param_env.and(self))
|
||||
pub fn is_copy_modulo_regions(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> bool {
|
||||
self.is_trivially_pure_clone_copy() || tcx.is_copy_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` have a size known at
|
||||
@ -1190,8 +1194,8 @@ impl<'tcx> Ty<'tcx> {
|
||||
/// over-approximation in generic contexts, where one can have
|
||||
/// strange rules like `<T as Foo<'static>>::Bar: Sized` that
|
||||
/// actually carry lifetime requirements.
|
||||
pub fn is_sized(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.is_trivially_sized(tcx) || tcx.is_sized_raw(param_env.and(self))
|
||||
pub fn is_sized(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
|
||||
self.is_trivially_sized(tcx) || tcx.is_sized_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implement the `Freeze`
|
||||
@ -1201,8 +1205,8 @@ impl<'tcx> Ty<'tcx> {
|
||||
/// optimization as well as the rules around static values. Note
|
||||
/// that the `Freeze` trait is not exposed to end users and is
|
||||
/// effectively an implementation detail.
|
||||
pub fn is_freeze(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.is_trivially_freeze() || tcx.is_freeze_raw(param_env.and(self))
|
||||
pub fn is_freeze(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
|
||||
self.is_trivially_freeze() || tcx.is_freeze_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type is `Freeze`.
|
||||
@ -1241,8 +1245,8 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implement the `Unpin` trait.
|
||||
pub fn is_unpin(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.is_trivially_unpin() || tcx.is_unpin_raw(param_env.and(self))
|
||||
pub fn is_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
|
||||
self.is_trivially_unpin() || tcx.is_unpin_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type is `Unpin`.
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! See docs in build/expr/mod.rs
|
||||
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use tracing::{debug, instrument};
|
||||
@ -9,6 +8,12 @@ use crate::build::expr::category::Category;
|
||||
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// Construct a temporary lifetime restricted to just the local scope
|
||||
pub(crate) fn local_temp_lifetime(&self) -> TempLifetime {
|
||||
let local_scope = self.local_scope();
|
||||
TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None }
|
||||
}
|
||||
|
||||
/// Returns an operand suitable for use until the end of the current
|
||||
/// scope expression.
|
||||
///
|
||||
@ -21,8 +26,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
block: BasicBlock,
|
||||
expr_id: ExprId,
|
||||
) -> BlockAnd<Operand<'tcx>> {
|
||||
let local_scope = self.local_scope();
|
||||
self.as_operand(block, Some(local_scope), expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
|
||||
self.as_operand(
|
||||
block,
|
||||
self.local_temp_lifetime(),
|
||||
expr_id,
|
||||
LocalInfo::Boring,
|
||||
NeedsTemporary::Maybe,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an operand suitable for use until the end of the current scope expression and
|
||||
@ -80,8 +90,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
block: BasicBlock,
|
||||
expr: ExprId,
|
||||
) -> BlockAnd<Operand<'tcx>> {
|
||||
let local_scope = self.local_scope();
|
||||
self.as_call_operand(block, Some(local_scope), expr)
|
||||
self.as_call_operand(block, self.local_temp_lifetime(), expr)
|
||||
}
|
||||
|
||||
/// Compile `expr` into a value that can be used as an operand.
|
||||
@ -102,7 +111,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
pub(crate) fn as_operand(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
scope: Option<region::Scope>,
|
||||
scope: TempLifetime,
|
||||
expr_id: ExprId,
|
||||
local_info: LocalInfo<'tcx>,
|
||||
needs_temporary: NeedsTemporary,
|
||||
@ -146,7 +155,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
pub(crate) fn as_call_operand(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
scope: Option<region::Scope>,
|
||||
scope: TempLifetime,
|
||||
expr_id: ExprId,
|
||||
) -> BlockAnd<Operand<'tcx>> {
|
||||
let this = self;
|
||||
@ -165,11 +174,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
if tcx.features().unsized_fn_params() {
|
||||
let ty = expr.ty;
|
||||
let param_env = this.param_env;
|
||||
|
||||
if !ty.is_sized(tcx, param_env) {
|
||||
if !ty.is_sized(tcx, this.typing_env()) {
|
||||
// !sized means !copy, so this is an unsized move
|
||||
assert!(!ty.is_copy_modulo_regions(tcx, param_env));
|
||||
assert!(!ty.is_copy_modulo_regions(tcx, this.typing_env()));
|
||||
|
||||
// As described above, detect the case where we are passing a value of unsized
|
||||
// type, and that value is coming from the deref of a box.
|
||||
|
@ -6,7 +6,6 @@ use std::iter;
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::hir::place::{Projection as HirProjection, ProjectionKind as HirProjectionKind};
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::AssertKind::BoundsCheck;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
@ -598,7 +597,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
index: ExprId,
|
||||
mutability: Mutability,
|
||||
fake_borrow_temps: Option<&mut Vec<Local>>,
|
||||
temp_lifetime: Option<region::Scope>,
|
||||
temp_lifetime: TempLifetime,
|
||||
expr_span: Span,
|
||||
source_info: SourceInfo,
|
||||
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||
|
@ -33,14 +33,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
expr_id: ExprId,
|
||||
) -> BlockAnd<Rvalue<'tcx>> {
|
||||
let local_scope = self.local_scope();
|
||||
self.as_rvalue(block, Some(local_scope), expr_id)
|
||||
self.as_rvalue(
|
||||
block,
|
||||
TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None },
|
||||
expr_id,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compile `expr`, yielding an rvalue.
|
||||
pub(crate) fn as_rvalue(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
scope: Option<region::Scope>,
|
||||
scope: TempLifetime,
|
||||
expr_id: ExprId,
|
||||
) -> BlockAnd<Rvalue<'tcx>> {
|
||||
let this = self;
|
||||
@ -171,7 +175,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
source_info,
|
||||
kind: StatementKind::StorageLive(result),
|
||||
});
|
||||
if let Some(scope) = scope {
|
||||
if let Some(scope) = scope.temp_lifetime {
|
||||
// schedule a shallow free of that memory, lest we unwind:
|
||||
this.schedule_drop_storage_and_value(expr_span, scope, result);
|
||||
}
|
||||
@ -445,7 +449,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
block = this.limit_capture_mutability(
|
||||
upvar_expr.span,
|
||||
upvar_expr.ty,
|
||||
scope,
|
||||
scope.temp_lifetime,
|
||||
block,
|
||||
arg,
|
||||
)
|
||||
@ -705,7 +709,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
value: ExprId,
|
||||
scope: Option<region::Scope>,
|
||||
scope: TempLifetime,
|
||||
outer_source_info: SourceInfo,
|
||||
) -> BlockAnd<Rvalue<'tcx>> {
|
||||
let this = self;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::middle::region::{Scope, ScopeData};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
@ -17,7 +16,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
pub(crate) fn as_temp(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
temp_lifetime: Option<region::Scope>,
|
||||
temp_lifetime: TempLifetime,
|
||||
expr_id: ExprId,
|
||||
mutability: Mutability,
|
||||
) -> BlockAnd<Local> {
|
||||
@ -31,7 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
fn as_temp_inner(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
temp_lifetime: Option<region::Scope>,
|
||||
temp_lifetime: TempLifetime,
|
||||
expr_id: ExprId,
|
||||
mutability: Mutability,
|
||||
) -> BlockAnd<Local> {
|
||||
@ -47,8 +46,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
let expr_ty = expr.ty;
|
||||
let deduplicate_temps =
|
||||
this.fixed_temps_scope.is_some() && this.fixed_temps_scope == temp_lifetime;
|
||||
let deduplicate_temps = this.fixed_temps_scope.is_some()
|
||||
&& this.fixed_temps_scope == temp_lifetime.temp_lifetime;
|
||||
let temp = if deduplicate_temps && let Some(temp_index) = this.fixed_temps.get(&expr_id) {
|
||||
*temp_index
|
||||
} else {
|
||||
@ -76,7 +75,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
LocalInfo::BlockTailTemp(tail_info)
|
||||
}
|
||||
|
||||
_ if let Some(Scope { data: ScopeData::IfThenRescope, id }) = temp_lifetime => {
|
||||
_ if let Some(Scope { data: ScopeData::IfThenRescope, id }) =
|
||||
temp_lifetime.temp_lifetime =>
|
||||
{
|
||||
LocalInfo::IfThenRescopeTemp {
|
||||
if_then: HirId { owner: this.hir_id.owner, local_id: id },
|
||||
}
|
||||
@ -117,7 +118,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// Anything with a shorter lifetime (e.g the `&foo()` in
|
||||
// `bar(&foo())` or anything within a block will keep the
|
||||
// regular drops just like runtime code.
|
||||
if let Some(temp_lifetime) = temp_lifetime {
|
||||
if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
|
||||
this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
|
||||
}
|
||||
}
|
||||
@ -125,10 +126,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
block = this.expr_into_dest(temp_place, block, expr_id).into_block();
|
||||
|
||||
if let Some(temp_lifetime) = temp_lifetime {
|
||||
if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
|
||||
this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
|
||||
}
|
||||
|
||||
if let Some(backwards_incompatible) = temp_lifetime.backwards_incompatible {
|
||||
this.schedule_backwards_incompatible_drop(expr_span, backwards_incompatible, temp);
|
||||
}
|
||||
|
||||
block.and(temp)
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// (#66975) Source could be a const of type `!`, so has to
|
||||
// exist in the generated MIR.
|
||||
unpack!(
|
||||
block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut)
|
||||
block =
|
||||
this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
|
||||
);
|
||||
|
||||
// This is an optimization. If the expression was a call then we already have an
|
||||
@ -321,7 +322,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let is_union = adt_def.is_union();
|
||||
let active_field_index = is_union.then(|| fields[0].name);
|
||||
|
||||
let scope = this.local_scope();
|
||||
let scope = this.local_temp_lifetime();
|
||||
|
||||
// first process the set of fields that were provided
|
||||
// (evaluating them in order given by user)
|
||||
@ -333,7 +334,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
unpack!(
|
||||
block = this.as_operand(
|
||||
block,
|
||||
Some(scope),
|
||||
scope,
|
||||
f.expr,
|
||||
LocalInfo::AggregateTemp,
|
||||
NeedsTemporary::Maybe,
|
||||
@ -548,15 +549,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
ExprKind::Yield { value } => {
|
||||
let scope = this.local_scope();
|
||||
let scope = this.local_temp_lifetime();
|
||||
let value = unpack!(
|
||||
block = this.as_operand(
|
||||
block,
|
||||
Some(scope),
|
||||
value,
|
||||
LocalInfo::Boring,
|
||||
NeedsTemporary::No
|
||||
)
|
||||
block =
|
||||
this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
|
||||
);
|
||||
let resume = this.cfg.start_new_block();
|
||||
this.cfg.terminate(block, source_info, TerminatorKind::Yield {
|
||||
|
@ -172,8 +172,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
None
|
||||
};
|
||||
|
||||
let temp =
|
||||
unpack!(block = this.as_temp(block, statement_scope, expr_id, Mutability::Not));
|
||||
let temp = unpack!(
|
||||
block = this.as_temp(
|
||||
block,
|
||||
TempLifetime {
|
||||
temp_lifetime: statement_scope,
|
||||
backwards_incompatible: None
|
||||
},
|
||||
expr_id,
|
||||
Mutability::Not
|
||||
)
|
||||
);
|
||||
|
||||
if let Some(span) = adjusted_span {
|
||||
this.local_decls[temp].source_info.span = span;
|
||||
|
@ -202,8 +202,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// Increment the decision depth, in case we encounter boolean expressions
|
||||
// further down.
|
||||
this.mcdc_increment_depth_if_enabled();
|
||||
let place =
|
||||
unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability));
|
||||
let place = unpack!(
|
||||
block = this.as_temp(
|
||||
block,
|
||||
TempLifetime {
|
||||
temp_lifetime: Some(temp_scope),
|
||||
backwards_incompatible: None
|
||||
},
|
||||
expr_id,
|
||||
mutability
|
||||
)
|
||||
);
|
||||
this.mcdc_decrement_depth_if_enabled();
|
||||
|
||||
let operand = Operand::Move(Place::from(place));
|
||||
|
@ -148,6 +148,10 @@ struct BlockContext(Vec<BlockFrame>);
|
||||
|
||||
struct Builder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
// FIXME(@lcnr): Why does this use an `infcx`, there should be
|
||||
// no shared type inference going on here. I feel like it would
|
||||
// clearer to manually construct one where necessary or to provide
|
||||
// a nice API for non-type inference trait system checks.
|
||||
infcx: InferCtxt<'tcx>,
|
||||
region_scope_tree: &'tcx region::ScopeTree,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -89,7 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::{ExprId, LintLevel};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_middle::{bug, span_bug, ty};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
@ -151,6 +151,9 @@ struct DropData {
|
||||
|
||||
/// Whether this is a value Drop or a StorageDead.
|
||||
kind: DropKind,
|
||||
|
||||
/// Whether this is a backwards-incompatible drop lint
|
||||
backwards_incompatible_lint: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -274,8 +277,12 @@ impl DropTree {
|
||||
// represents the block in the tree that should be jumped to once all
|
||||
// of the required drops have been performed.
|
||||
let fake_source_info = SourceInfo::outermost(DUMMY_SP);
|
||||
let fake_data =
|
||||
DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage };
|
||||
let fake_data = DropData {
|
||||
source_info: fake_source_info,
|
||||
local: Local::MAX,
|
||||
kind: DropKind::Storage,
|
||||
backwards_incompatible_lint: false,
|
||||
};
|
||||
let drops = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]);
|
||||
Self { drops, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() }
|
||||
}
|
||||
@ -763,7 +770,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let local =
|
||||
place.as_local().unwrap_or_else(|| bug!("projection in tail call args"));
|
||||
|
||||
Some(DropData { source_info, local, kind: DropKind::Value })
|
||||
Some(DropData {
|
||||
source_info,
|
||||
local,
|
||||
kind: DropKind::Value,
|
||||
backwards_incompatible_lint: false,
|
||||
})
|
||||
}
|
||||
Operand::Constant(_) => None,
|
||||
})
|
||||
@ -1019,9 +1031,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
if local.index() <= self.arg_count {
|
||||
span_bug!(
|
||||
span,
|
||||
"`schedule_drop` called with local {:?} and arg_count {}",
|
||||
"`schedule_drop` called with body argument {:?} \
|
||||
but its storage does not require a drop",
|
||||
local,
|
||||
self.arg_count,
|
||||
)
|
||||
}
|
||||
false
|
||||
@ -1089,6 +1101,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
|
||||
local,
|
||||
kind: drop_kind,
|
||||
backwards_incompatible_lint: false,
|
||||
});
|
||||
|
||||
return;
|
||||
@ -1098,6 +1111,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
|
||||
}
|
||||
|
||||
/// Schedule emission of a backwards incompatible drop lint hint.
|
||||
/// Applicable only to temporary values for now.
|
||||
pub(crate) fn schedule_backwards_incompatible_drop(
|
||||
&mut self,
|
||||
span: Span,
|
||||
region_scope: region::Scope,
|
||||
local: Local,
|
||||
) {
|
||||
if !self.local_decls[local].ty.has_significant_drop(self.tcx, ty::TypingEnv {
|
||||
typing_mode: ty::TypingMode::non_body_analysis(),
|
||||
param_env: self.param_env,
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
for scope in self.scopes.scopes.iter_mut().rev() {
|
||||
// Since we are inserting linting MIR statement, we have to invalidate the caches
|
||||
scope.invalidate_cache();
|
||||
if scope.region_scope == region_scope {
|
||||
let region_scope_span = region_scope.span(self.tcx, self.region_scope_tree);
|
||||
let scope_end = self.tcx.sess.source_map().end_point(region_scope_span);
|
||||
|
||||
scope.drops.push(DropData {
|
||||
source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
|
||||
local,
|
||||
kind: DropKind::Value,
|
||||
backwards_incompatible_lint: true,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
span_bug!(
|
||||
span,
|
||||
"region scope {:?} not in scope to drop {:?} for linting",
|
||||
region_scope,
|
||||
local
|
||||
);
|
||||
}
|
||||
|
||||
/// Indicates that the "local operand" stored in `local` is
|
||||
/// *moved* at some point during execution (see `local_scope` for
|
||||
/// more information about what a "local operand" is -- in short,
|
||||
@ -1378,16 +1430,25 @@ fn build_scope_drops<'tcx>(
|
||||
continue;
|
||||
}
|
||||
|
||||
unwind_drops.add_entry_point(block, unwind_to);
|
||||
|
||||
let next = cfg.start_new_block();
|
||||
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
||||
place: local.into(),
|
||||
target: next,
|
||||
unwind: UnwindAction::Continue,
|
||||
replace: false,
|
||||
});
|
||||
block = next;
|
||||
if drop_data.backwards_incompatible_lint {
|
||||
cfg.push(block, Statement {
|
||||
source_info,
|
||||
kind: StatementKind::BackwardIncompatibleDropHint {
|
||||
place: Box::new(local.into()),
|
||||
reason: BackwardIncompatibleDropReason::Edition2024,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
unwind_drops.add_entry_point(block, unwind_to);
|
||||
let next = cfg.start_new_block();
|
||||
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
||||
place: local.into(),
|
||||
target: next,
|
||||
unwind: UnwindAction::Continue,
|
||||
replace: false,
|
||||
});
|
||||
block = next;
|
||||
}
|
||||
}
|
||||
DropKind::Storage => {
|
||||
if storage_dead_on_unwind {
|
||||
|
@ -11,7 +11,7 @@ use rustc_middle::span_bug;
|
||||
use rustc_middle::thir::visit::Visitor;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
@ -37,7 +37,7 @@ struct UnsafetyVisitor<'a, 'tcx> {
|
||||
/// of the LHS and the span of the assignment expression.
|
||||
assignment_info: Option<Ty<'tcx>>,
|
||||
in_union_destructure: bool,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
inside_adt: bool,
|
||||
warnings: &'a mut Vec<UnusedUnsafeWarning>,
|
||||
|
||||
@ -213,7 +213,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
|
||||
body_target_features: self.body_target_features,
|
||||
assignment_info: self.assignment_info,
|
||||
in_union_destructure: false,
|
||||
param_env: self.param_env,
|
||||
typing_env: self.typing_env,
|
||||
inside_adt: false,
|
||||
warnings: self.warnings,
|
||||
suggest_unsafe_block: self.suggest_unsafe_block,
|
||||
@ -370,7 +370,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||
};
|
||||
match rm {
|
||||
Mutability::Not => {
|
||||
if !ty.is_freeze(self.tcx, self.param_env) {
|
||||
if !ty.is_freeze(self.tcx, self.typing_env) {
|
||||
self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
|
||||
}
|
||||
}
|
||||
@ -570,9 +570,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||
&& adt_def.is_union()
|
||||
{
|
||||
if let Some(assigned_ty) = self.assignment_info {
|
||||
if assigned_ty
|
||||
.needs_drop(self.tcx, ty::TypingEnv::from_param_env(self.param_env))
|
||||
{
|
||||
if assigned_ty.needs_drop(self.tcx, self.typing_env) {
|
||||
// This would be unsafe, but should be outright impossible since we
|
||||
// reject such unions.
|
||||
assert!(
|
||||
@ -612,7 +610,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||
if visitor.found {
|
||||
match borrow_kind {
|
||||
BorrowKind::Fake(_) | BorrowKind::Shared
|
||||
if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
|
||||
if !self.thir[arg].ty.is_freeze(self.tcx, self.typing_env) =>
|
||||
{
|
||||
self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
|
||||
}
|
||||
@ -1030,7 +1028,8 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
|
||||
body_target_features,
|
||||
assignment_info: None,
|
||||
in_union_destructure: false,
|
||||
param_env: tcx.param_env(def),
|
||||
// FIXME(#132279): we're clearly in a body here.
|
||||
typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
|
||||
inside_adt: false,
|
||||
warnings: &mut warnings,
|
||||
suggest_unsafe_block: true,
|
||||
|
@ -132,21 +132,16 @@ impl<'tcx> TerminatorClassifier<'tcx> for CallRecursion<'tcx> {
|
||||
return false;
|
||||
}
|
||||
let caller = body.source.def_id();
|
||||
let param_env = tcx.param_env(caller);
|
||||
let typing_env = body.typing_env(tcx);
|
||||
|
||||
let func_ty = func.ty(body, tcx);
|
||||
if let ty::FnDef(callee, args) = *func_ty.kind() {
|
||||
let Ok(normalized_args) =
|
||||
tcx.try_normalize_erasing_regions(ty::TypingEnv::from_param_env(param_env), args)
|
||||
else {
|
||||
let Ok(normalized_args) = tcx.try_normalize_erasing_regions(typing_env, args) else {
|
||||
return false;
|
||||
};
|
||||
let (callee, call_args) = if let Ok(Some(instance)) = Instance::try_resolve(
|
||||
tcx,
|
||||
ty::TypingEnv::from_param_env(param_env),
|
||||
callee,
|
||||
normalized_args,
|
||||
) {
|
||||
let (callee, call_args) = if let Ok(Some(instance)) =
|
||||
Instance::try_resolve(tcx, typing_env, callee, normalized_args)
|
||||
{
|
||||
(instance.def_id(), instance.args)
|
||||
} else {
|
||||
(callee, normalized_args)
|
||||
|
@ -23,7 +23,6 @@ use tracing::{debug, info, instrument, trace};
|
||||
|
||||
use crate::errors;
|
||||
use crate::thir::cx::Cx;
|
||||
use crate::thir::cx::region::Scope;
|
||||
use crate::thir::util::UserAnnotatedTyHelpers;
|
||||
|
||||
impl<'tcx> Cx<'tcx> {
|
||||
@ -240,7 +239,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
fn mirror_expr_cast(
|
||||
&mut self,
|
||||
source: &'tcx hir::Expr<'tcx>,
|
||||
temp_lifetime: Option<Scope>,
|
||||
temp_lifetime: TempLifetime,
|
||||
span: Span,
|
||||
) -> ExprKind<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
@ -325,7 +324,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let expr_ty = self.typeck_results().expr_ty(expr);
|
||||
let temp_lifetime =
|
||||
let (temp_lifetime, backwards_incompatible) =
|
||||
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
|
||||
|
||||
let kind = match expr.kind {
|
||||
@ -361,7 +360,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
|
||||
let tupled_args = Expr {
|
||||
ty: Ty::new_tup_from_iter(tcx, arg_tys),
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
span: expr.span,
|
||||
kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
|
||||
};
|
||||
@ -391,7 +390,10 @@ impl<'tcx> Cx<'tcx> {
|
||||
&& let [value] = args
|
||||
{
|
||||
return Expr {
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime {
|
||||
temp_lifetime,
|
||||
backwards_incompatible,
|
||||
},
|
||||
ty: expr_ty,
|
||||
span: expr.span,
|
||||
kind: ExprKind::Box { value: self.mirror_expr(value) },
|
||||
@ -811,13 +813,13 @@ impl<'tcx> Cx<'tcx> {
|
||||
},
|
||||
hir::ExprKind::Loop(body, ..) => {
|
||||
let block_ty = self.typeck_results().node_type(body.hir_id);
|
||||
let temp_lifetime = self
|
||||
let (temp_lifetime, backwards_incompatible) = self
|
||||
.rvalue_scopes
|
||||
.temporary_scope(self.region_scope_tree, body.hir_id.local_id);
|
||||
let block = self.mirror_block(body);
|
||||
let body = self.thir.exprs.push(Expr {
|
||||
ty: block_ty,
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
span: self.thir[block].span,
|
||||
kind: ExprKind::Block { block },
|
||||
});
|
||||
@ -838,13 +840,17 @@ impl<'tcx> Cx<'tcx> {
|
||||
expr, cast_ty.hir_id, user_ty,
|
||||
);
|
||||
|
||||
let cast = self.mirror_expr_cast(source, temp_lifetime, expr.span);
|
||||
let cast = self.mirror_expr_cast(
|
||||
source,
|
||||
TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
expr.span,
|
||||
);
|
||||
|
||||
if let Some(user_ty) = user_ty {
|
||||
// NOTE: Creating a new Expr and wrapping a Cast inside of it may be
|
||||
// inefficient, revisit this when performance becomes an issue.
|
||||
let cast_expr = self.thir.exprs.push(Expr {
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty: expr_ty,
|
||||
span: expr.span,
|
||||
kind: cast,
|
||||
@ -887,7 +893,12 @@ impl<'tcx> Cx<'tcx> {
|
||||
hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"),
|
||||
};
|
||||
|
||||
Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind }
|
||||
Expr {
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty: expr_ty,
|
||||
span: expr.span,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
fn user_args_applied_to_res(
|
||||
@ -931,7 +942,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
span: Span,
|
||||
overloaded_callee: Option<Ty<'tcx>>,
|
||||
) -> Expr<'tcx> {
|
||||
let temp_lifetime =
|
||||
let (temp_lifetime, backwards_incompatible) =
|
||||
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
|
||||
let (ty, user_ty) = match overloaded_callee {
|
||||
Some(fn_def) => (fn_def, None),
|
||||
@ -952,7 +963,12 @@ impl<'tcx> Cx<'tcx> {
|
||||
)
|
||||
}
|
||||
};
|
||||
Expr { temp_lifetime, ty, span, kind: ExprKind::ZstLiteral { user_ty } }
|
||||
Expr {
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty,
|
||||
span,
|
||||
kind: ExprKind::ZstLiteral { user_ty },
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId {
|
||||
@ -1025,7 +1041,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
Res::Def(DefKind::Static { .. }, id) => {
|
||||
// this is &raw for extern static or static mut, and & for other statics
|
||||
let ty = self.tcx.static_ptr_ty(id, self.typing_env());
|
||||
let temp_lifetime = self
|
||||
let (temp_lifetime, backwards_incompatible) = self
|
||||
.rvalue_scopes
|
||||
.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
|
||||
let kind = if self.tcx.is_thread_local_static(id) {
|
||||
@ -1035,7 +1051,12 @@ impl<'tcx> Cx<'tcx> {
|
||||
ExprKind::StaticRef { alloc_id, ty, def_id: id }
|
||||
};
|
||||
ExprKind::Deref {
|
||||
arg: self.thir.exprs.push(Expr { ty, temp_lifetime, span: expr.span, kind }),
|
||||
arg: self.thir.exprs.push(Expr {
|
||||
ty,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
span: expr.span,
|
||||
kind,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1106,13 +1127,13 @@ impl<'tcx> Cx<'tcx> {
|
||||
|
||||
// construct the complete expression `foo()` for the overloaded call,
|
||||
// which will yield the &T type
|
||||
let temp_lifetime =
|
||||
let (temp_lifetime, backwards_incompatible) =
|
||||
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
|
||||
let fun = self.method_callee(expr, span, overloaded_callee);
|
||||
let fun = self.thir.exprs.push(fun);
|
||||
let fun_ty = self.thir[fun].ty;
|
||||
let ref_expr = self.thir.exprs.push(Expr {
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty: ref_ty,
|
||||
span,
|
||||
kind: ExprKind::Call { ty: fun_ty, fun, args, from_hir_call: false, fn_span: span },
|
||||
@ -1127,7 +1148,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
closure_expr: &'tcx hir::Expr<'tcx>,
|
||||
place: HirPlace<'tcx>,
|
||||
) -> Expr<'tcx> {
|
||||
let temp_lifetime = self
|
||||
let (temp_lifetime, backwards_incompatible) = self
|
||||
.rvalue_scopes
|
||||
.temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
|
||||
let var_ty = place.base_ty;
|
||||
@ -1143,7 +1164,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
};
|
||||
|
||||
let mut captured_place_expr = Expr {
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty: var_ty,
|
||||
span: closure_expr.span,
|
||||
kind: self.convert_var(var_hir_id),
|
||||
@ -1168,8 +1189,12 @@ impl<'tcx> Cx<'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
captured_place_expr =
|
||||
Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
|
||||
captured_place_expr = Expr {
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty: proj.ty,
|
||||
span: closure_expr.span,
|
||||
kind,
|
||||
};
|
||||
}
|
||||
|
||||
captured_place_expr
|
||||
@ -1184,7 +1209,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
let upvar_capture = captured_place.info.capture_kind;
|
||||
let captured_place_expr =
|
||||
self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
|
||||
let temp_lifetime = self
|
||||
let (temp_lifetime, backwards_incompatible) = self
|
||||
.rvalue_scopes
|
||||
.temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
|
||||
|
||||
@ -1201,7 +1226,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
}
|
||||
};
|
||||
Expr {
|
||||
temp_lifetime,
|
||||
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
|
||||
ty: upvar_ty,
|
||||
span: closure_expr.span,
|
||||
kind: ExprKind::Borrow {
|
||||
|
@ -123,7 +123,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
|
||||
pat_from_hir(self.tcx, self.param_env, self.typeck_results(), p)
|
||||
pat_from_hir(self.tcx, self.typing_env(), self.typeck_results(), p)
|
||||
}
|
||||
|
||||
fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option<Param<'tcx>> {
|
||||
|
@ -7,6 +7,7 @@ use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_e
|
||||
use rustc_hir::def::*;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
|
||||
use rustc_infer::traits::Reveal;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::limits::get_limit_size;
|
||||
use rustc_middle::thir::visit::Visitor;
|
||||
@ -191,6 +192,15 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||
fn typing_env(&self) -> ty::TypingEnv<'tcx> {
|
||||
// FIXME(#132279): We're in a body, should handle opaques.
|
||||
debug_assert_eq!(self.param_env.reveal(), Reveal::UserFacing);
|
||||
ty::TypingEnv {
|
||||
typing_mode: ty::TypingMode::non_body_analysis(),
|
||||
param_env: self.param_env,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, f))]
|
||||
fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
|
||||
let old_let_source = self.let_source;
|
||||
@ -775,7 +785,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
|
||||
return;
|
||||
};
|
||||
|
||||
let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
|
||||
let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env());
|
||||
|
||||
let sess = cx.tcx.sess;
|
||||
|
||||
|
@ -2,11 +2,11 @@ use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_hir as hir;
|
||||
use rustc_index::Idx;
|
||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, ValTree};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
|
||||
use rustc_middle::{mir, span_bug};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::ObligationCause;
|
||||
@ -35,10 +35,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
id: hir::HirId,
|
||||
span: Span,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
// FIXME(#132279): We likely want to be able to reveal the hidden types
|
||||
// of opaques defined in this function here.
|
||||
let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let mut convert = ConstToPat::new(self, id, span, infcx);
|
||||
let mut convert = ConstToPat::new(self, id, span);
|
||||
|
||||
match c.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
|
||||
@ -49,27 +46,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
struct ConstToPat<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
span: Span,
|
||||
|
||||
// inference context used for checking `T: Structural` bounds.
|
||||
infcx: InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
treat_byte_string_as_slice: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> ConstToPat<'tcx> {
|
||||
fn new(
|
||||
pat_ctxt: &PatCtxt<'_, 'tcx>,
|
||||
id: hir::HirId,
|
||||
span: Span,
|
||||
infcx: InferCtxt<'tcx>,
|
||||
) -> Self {
|
||||
fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self {
|
||||
trace!(?pat_ctxt.typeck_results.hir_owner);
|
||||
ConstToPat {
|
||||
tcx: pat_ctxt.tcx,
|
||||
typing_env: pat_ctxt.typing_env,
|
||||
span,
|
||||
infcx,
|
||||
param_env: pat_ctxt.param_env,
|
||||
treat_byte_string_as_slice: pat_ctxt
|
||||
.typeck_results
|
||||
.treat_byte_string_as_slice
|
||||
@ -77,16 +67,8 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn typing_env(&self) -> ty::TypingEnv<'tcx> {
|
||||
self.infcx.typing_env(self.param_env)
|
||||
}
|
||||
|
||||
fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_structural_eq_shallow(self.infcx.tcx)
|
||||
ty.is_structural_eq_shallow(self.tcx)
|
||||
}
|
||||
|
||||
fn unevaluated_to_pat(
|
||||
@ -105,22 +87,21 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
// FIXME: `const_eval_resolve_for_typeck` should probably just set the env to `Reveal::All`
|
||||
// instead of having this logic here
|
||||
let typing_env =
|
||||
self.tcx().erase_regions(self.typing_env()).with_reveal_all_normalized(self.tcx());
|
||||
let uv = self.tcx().erase_regions(uv);
|
||||
self.tcx.erase_regions(self.typing_env).with_reveal_all_normalized(self.tcx);
|
||||
let uv = self.tcx.erase_regions(uv);
|
||||
|
||||
// try to resolve e.g. associated constants to their definition on an impl, and then
|
||||
// evaluate the const.
|
||||
let valtree = match self.infcx.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span)
|
||||
{
|
||||
let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
|
||||
Ok(Ok(c)) => c,
|
||||
Err(ErrorHandled::Reported(_, _)) => {
|
||||
// Let's tell the use where this failing const occurs.
|
||||
let e = self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span });
|
||||
let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span });
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
}
|
||||
Err(ErrorHandled::TooGeneric(_)) => {
|
||||
let e = self
|
||||
.tcx()
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(ConstPatternDependsOnGenericParameter { span: self.span });
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
@ -130,13 +111,13 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
let e = match bad_ty.kind() {
|
||||
ty::Adt(def, ..) => {
|
||||
assert!(def.is_union());
|
||||
self.tcx().dcx().emit_err(UnionPattern { span: self.span })
|
||||
self.tcx.dcx().emit_err(UnionPattern { span: self.span })
|
||||
}
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => {
|
||||
self.tcx().dcx().emit_err(PointerPattern { span: self.span })
|
||||
self.tcx.dcx().emit_err(PointerPattern { span: self.span })
|
||||
}
|
||||
_ => self
|
||||
.tcx()
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }),
|
||||
};
|
||||
@ -151,7 +132,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
// Always check for `PartialEq` if we had no other errors yet.
|
||||
if !self.type_has_partial_eq_impl(ty) {
|
||||
let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty };
|
||||
let e = self.tcx().dcx().emit_err(err);
|
||||
let e = self.tcx.dcx().emit_err(err);
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
}
|
||||
}
|
||||
@ -161,18 +142,19 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
|
||||
let tcx = self.tcx();
|
||||
let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
|
||||
// double-check there even *is* a semantic `PartialEq` to dispatch to.
|
||||
//
|
||||
// (If there isn't, then we can safely issue a hard
|
||||
// error, because that's never worked, due to compiler
|
||||
// using `PartialEq::eq` in this scenario in the past.)
|
||||
let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span));
|
||||
let partial_eq_trait_id =
|
||||
self.tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span));
|
||||
let partial_eq_obligation = Obligation::new(
|
||||
tcx,
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
self.param_env,
|
||||
ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
|
||||
param_env,
|
||||
ty::TraitRef::new(self.tcx, partial_eq_trait_id, [ty, ty]),
|
||||
);
|
||||
|
||||
// This *could* accept a type that isn't actually `PartialEq`, because region bounds get
|
||||
@ -181,7 +163,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
// `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
|
||||
// we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
|
||||
// can ensure that the type really implements `PartialEq`.
|
||||
self.infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation)
|
||||
infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation)
|
||||
}
|
||||
|
||||
fn field_pats(
|
||||
@ -192,7 +174,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
.map(|(idx, (val, ty))| {
|
||||
let field = FieldIdx::new(idx);
|
||||
// Patterns can only use monomorphic types.
|
||||
let ty = self.tcx().normalize_erasing_regions(self.typing_env(), ty);
|
||||
let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
|
||||
FieldPat { field, pattern: self.valtree_to_pat(val, ty) }
|
||||
})
|
||||
.collect()
|
||||
@ -202,14 +184,12 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
|
||||
let span = self.span;
|
||||
let tcx = self.tcx();
|
||||
let param_env = self.param_env;
|
||||
|
||||
let tcx = self.tcx;
|
||||
let kind = match ty.kind() {
|
||||
ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
|
||||
// Extremely important check for all ADTs! Make sure they opted-in to be used in
|
||||
// patterns.
|
||||
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,);
|
||||
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
|
||||
let err = TypeNotStructural { span, non_sm_ty: ty };
|
||||
let e = tcx.dcx().emit_err(err);
|
||||
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
@ -227,7 +207,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
adt_def.variants()[variant_index]
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx(), args)),
|
||||
.map(|field| field.ty(self.tcx, args)),
|
||||
),
|
||||
),
|
||||
}
|
||||
@ -235,14 +215,9 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
ty::Adt(def, args) => {
|
||||
assert!(!def.is_union()); // Valtree construction would never succeed for unions.
|
||||
PatKind::Leaf {
|
||||
subpatterns: self.field_pats(
|
||||
cv.unwrap_branch().iter().copied().zip(
|
||||
def.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx(), args)),
|
||||
),
|
||||
),
|
||||
subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
|
||||
def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx, args)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
ty::Tuple(fields) => PatKind::Leaf {
|
||||
@ -276,7 +251,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
||||
// convert the dereferenced constant to a pattern that is the sub-pattern of the
|
||||
// deref pattern.
|
||||
_ => {
|
||||
if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
|
||||
if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
|
||||
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
|
||||
let e = tcx.dcx().emit_err(err);
|
||||
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
|
@ -30,7 +30,7 @@ use crate::thir::util::UserAnnotatedTyHelpers;
|
||||
|
||||
struct PatCtxt<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
|
||||
/// Used by the Rust 2024 migration lint.
|
||||
@ -39,13 +39,13 @@ struct PatCtxt<'a, 'tcx> {
|
||||
|
||||
pub(super) fn pat_from_hir<'a, 'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
pat: &'tcx hir::Pat<'tcx>,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
let mut pcx = PatCtxt {
|
||||
tcx,
|
||||
param_env,
|
||||
typing_env,
|
||||
typeck_results,
|
||||
rust_2024_migration_suggestion: typeck_results
|
||||
.rust_2024_migration_desugared_pats()
|
||||
@ -251,7 +251,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
|
||||
let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
|
||||
|
||||
let cmp = lo.compare_with(hi, ty, self.tcx, ty::TypingEnv::from_param_env(self.param_env));
|
||||
let cmp = lo.compare_with(hi, ty, self.tcx, self.typing_env);
|
||||
let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
|
||||
match (end, cmp) {
|
||||
// `x..y` where `x < y`.
|
||||
|
@ -253,6 +253,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::Nop => None,
|
||||
};
|
||||
if let Some(destination) = destination {
|
||||
|
@ -157,6 +157,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
||||
| StatementKind::Nop
|
||||
| StatementKind::Retag(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::StorageLive(..) => {}
|
||||
}
|
||||
}
|
||||
|
@ -385,6 +385,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,31 @@ mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspen
|
||||
.help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point
|
||||
mir_transform_operation_will_panic = this operation will panic at runtime
|
||||
|
||||
mir_transform_tail_expr_drop_order = relative drop order changing in Rust 2024
|
||||
.temporaries = in Rust 2024, this temporary value will be dropped first
|
||||
.observers = in Rust 2024, this local variable or temporary value will be dropped second
|
||||
.note_dtors =
|
||||
dropping the temporary value runs this custom `Drop` impl, which we could not prove to be side-effect free
|
||||
.note_observer_dtors =
|
||||
dropping the local runs this custom `Drop` impl, which we could not prove to be side-effect free
|
||||
.drop_location =
|
||||
now the temporary value is dropped here, before the local variables in the block or statement
|
||||
.note_epilogue = most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||
.label_local_epilogue = {$is_dropped_first_edition_2024 ->
|
||||
[true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024
|
||||
*[false] `{$name}` will be dropped later as of Edition 2024
|
||||
}
|
||||
|
||||
mir_transform_tail_expr_dtor = {$dtor_kind ->
|
||||
[dyn] `{$name}` may invoke a custom destructor because it contains a trait object
|
||||
*[concrete] `{$name}` invokes this custom destructor
|
||||
}
|
||||
|
||||
mir_transform_tail_expr_local = {$is_generated_name ->
|
||||
[true] this value will be stored in a temporary; let us call it `{$name}`
|
||||
*[false] `{$name}` calls a custom destructor
|
||||
}
|
||||
|
||||
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)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user