diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index bca4d8fa7b5ff..04fee06e2434c 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -589,6 +589,7 @@ pub enum RustcDumpLayoutKind { #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] pub enum RustcMirKind { PrettyLiveLocals, + PrettyPreciseLiveness, PrettyTransitiveLiveLocals, PeekMaybeInit, PeekMaybeUninit, diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index b9c03883d7c0b..70e5e4803c362 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -672,6 +672,10 @@ impl CombineAttributeParser for RustcMirParser { cx.expect_no_args(mi.args())?; Some(RustcMirKind::PrettyLiveLocals) } + sym::rustc_pretty_precise_liveness => { + cx.expect_no_args(mi.args())?; + Some(RustcMirKind::PrettyPreciseLiveness) + } sym::rustc_pretty_transitive_live_locals => { cx.expect_no_args(mi.args())?; Some(RustcMirKind::PrettyTransitiveLiveLocals) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 7f46b19f7568f..e2446d94993a7 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -16,7 +16,7 @@ use rustc_abi::{CanonAbi, ExternAbi, X86Call}; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_codegen_ssa::diagnostics::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ShimKind, TypeVisitableExt}; use rustc_session::Session; @@ -441,7 +441,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let instance = if let ty::FnDef(def_id, fn_args) = *func.layout().ty.kind() { let instance = ty::Instance::expect_resolve( fx.tcx, - ty::TypingEnv::fully_monomorphized(), + fx.typing_env(), def_id, fn_args.no_bound_vars().unwrap(), source_info.span, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index a6d5e4c6a6423..6828c6cd0ab4f 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -720,7 +720,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let func_ref = fx.get_function_ref( Instance::resolve_for_fn_ptr( fx.tcx, - ty::TypingEnv::fully_monomorphized(), + fx.typing_env(), def_id, args.no_bound_vars().unwrap(), ) @@ -771,8 +771,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { ty.builtin_deref(true).is_some_and(|pointee_ty| { - fx.tcx - .type_has_metadata(pointee_ty, ty::TypingEnv::fully_monomorphized()) + fx.tcx.type_has_metadata(pointee_ty, fx.typing_env()) }) } diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 94a71c65ce261..d59fb0f93ed4f 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -4,8 +4,8 @@ use rustc_abi::{Float, Integer, Primitive}; use rustc_index::IndexVec; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::layout::{ - self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, - codegen_handle_fn_abi_err, + self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv as _, LayoutError, + LayoutOfHelpers, codegen_handle_fn_abi_err, }; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; @@ -351,7 +351,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { { self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, - ty::TypingEnv::fully_monomorphized(), + self.typing_env(), ty::EarlyBinder::bind(self.tcx, value), ) } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index f1e4fd818ebeb..5fc02a19c0094 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -9,6 +9,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{ AllocId, GlobalAlloc, PointerArithmetic, Scalar, read_target_uint, }; +use rustc_middle::ty::layout::HasTypingEnv as _; use rustc_middle::ty::{ExistentialTraitRef, ScalarInt}; use crate::prelude::*; @@ -81,7 +82,7 @@ pub(crate) fn eval_mir_constant<'tcx>( let cv = fx.monomorphize(constant.const_); // This cannot fail because we checked all required_consts in advance. let val = cv - .eval(fx.tcx, ty::TypingEnv::fully_monomorphized(), constant.span) + .eval(fx.tcx, fx.typing_env(), constant.span) .expect("erroneous constant missed by mono item collection"); (val, cv.ty()) } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index b2bf856e7bcee..c13af35f94108 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -7,7 +7,7 @@ use rustc_abi::CanonAbi; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_attr_ir::lang_items::LangItem; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar as ConstScalar}; -use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _}; use rustc_span::sym; use rustc_target::asm::*; use rustc_target::spec::Arch; @@ -175,7 +175,7 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( if let ty::FnDef(def_id, args) = *const_.ty().kind() { let instance = ty::Instance::resolve_for_fn_ptr( fx.tcx, - ty::TypingEnv::fully_monomorphized(), + fx.typing_env(), def_id, args.no_bound_vars().unwrap(), ) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 9c3182c944dc6..1437ea7445d24 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -22,7 +22,7 @@ use cranelift_codegen::ir::{ }; use rustc_middle::ty; use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::layout::ValidityRequirement; +use rustc_middle::ty::layout::{HasTypingEnv as _, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_span::{Spanned, Symbol, sym}; use rustc_target::spec::PanicStrategy; @@ -729,10 +729,7 @@ fn codegen_regular_intrinsic_call<'tcx>( if let Some(requirement) = requirement { let do_panic = !fx .tcx - .check_validity_requirement(( - requirement, - ty::TypingEnv::fully_monomorphized().as_query_input(ty), - )) + .check_validity_requirement((requirement, fx.typing_env().as_query_input(ty))) .expect("expect to have layout during codegen"); if do_panic { diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index f1c44df1f6886..317e61031b34c 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -1,5 +1,7 @@ //! Various operations on integer and floating-point numbers +use rustc_middle::ty::layout::HasTypingEnv as _; + use crate::codegen_f16_f128; use crate::prelude::*; @@ -437,7 +439,7 @@ fn codegen_ptr_binop<'tcx>( .layout() .ty .builtin_deref(true) - .map(|ty| !fx.tcx.type_has_metadata(ty, ty::TypingEnv::fully_monomorphized())) + .map(|ty| !fx.tcx.type_has_metadata(ty, fx.typing_env())) .unwrap_or(true); if is_thin_ptr { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 440ae9c4b812e..a4af73cbc3f20 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -693,7 +693,7 @@ impl<'tcx> CPlace<'tcx> { }; let (field_ptr, field_layout) = codegen_field(fx, base, extra, layout, field); - if fx.tcx.type_has_metadata(field_layout.ty, ty::TypingEnv::fully_monomorphized()) { + if fx.tcx.type_has_metadata(field_layout.ty, fx.typing_env()) { CPlace::for_ptr_with_extra(field_ptr, extra.unwrap(), field_layout) } else { CPlace::for_ptr(field_ptr, field_layout) @@ -808,7 +808,7 @@ impl<'tcx> CPlace<'tcx> { pub(crate) fn place_deref(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> CPlace<'tcx> { let inner_layout = fx.layout_of(self.layout().ty.builtin_deref(true).unwrap()); - if fx.tcx.type_has_metadata(inner_layout.ty, ty::TypingEnv::fully_monomorphized()) { + if fx.tcx.type_has_metadata(inner_layout.ty, fx.typing_env()) { let (addr, extra) = self.to_cvalue(fx).load_scalar_pair(fx); CPlace::for_ptr_with_extra(Pointer::new(addr), extra, inner_layout) } else { @@ -821,7 +821,7 @@ impl<'tcx> CPlace<'tcx> { fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CValue<'tcx> { - if fx.tcx.type_has_metadata(self.layout().ty, ty::TypingEnv::fully_monomorphized()) { + if fx.tcx.type_has_metadata(self.layout().ty, fx.typing_env()) { let (ptr, extra) = self.to_ptr_unsized(); CValue::by_val_pair(ptr.get_addr(fx), extra, layout) } else { diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index fb86b8a7f905e..902f8ba52faaa 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -16,8 +16,8 @@ use rustc_middle::mir::interpret::{ self, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, read_target_uint, }; use rustc_middle::mono::MonoItem; -use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Instance}; +use rustc_middle::ty::Instance; +use rustc_middle::ty::layout::{HasTypingEnv as _, LayoutOf}; use rustc_span::def_id::DefId; use rustc_span::{bug, span_bug}; @@ -251,7 +251,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let gcc_type = if nested { self.type_i8() } else { - let ty = instance.ty(self.tcx, ty::TypingEnv::fully_monomorphized()); + let ty = instance.ty(self.tcx, self.typing_env()); self.layout_of(ty).gcc_type(self) }; diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index de8762d1c06b4..eaf44c35ded01 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -10,7 +10,8 @@ use gccjit::{ use rustc_abi::{CanonAbi, Endian, ExternAbi}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::HasTypingEnv as _; use rustc_target::callconv::{ArgAbi, ArgAttributes, FnAbi, PassMode}; use rustc_type_ir::{Interner, TyKind}; @@ -387,10 +388,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { 128 => self.tcx.types.i128, _ => unreachable!("unexpected integer size"), }; - let layout = self - .tcx - .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(res_ty)) - .unwrap(); + let layout = self.tcx.layout_of(self.cx.typing_env().as_query_input(res_ty)).unwrap(); let arg_abi = ArgAbi { layout, mode: PassMode::Direct(ArgAttributes::new()) }; let mut fn_abi = FnAbi { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 54013b6be6173..959ad826359f9 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -16,7 +16,7 @@ use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, LayoutTy use rustc_hir as hir; use rustc_middle::mir::BinOp; use rustc_middle::ty::consts::ConstExt; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv as _, LayoutOf}; use rustc_middle::ty::{self, Ty}; use rustc_span::{ErrorGuaranteed, Span, Symbol, span_bug, sym}; @@ -540,7 +540,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( match *in_elem.kind() { ty::RawPtr(p_ty, _) => { let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { - bx.tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) + bx.tcx.normalize_erasing_regions(bx.typing_env(), ty) }); require!( metadata.is_unit(), @@ -554,7 +554,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( match *out_elem.kind() { ty::RawPtr(p_ty, _) => { let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { - bx.tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) + bx.tcx.normalize_erasing_regions(bx.typing_env(), ty) }); require!( metadata.is_unit(), diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 03058ba02d9dc..3b1e36265b941 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -10,7 +10,7 @@ use rustc_codegen_ssa::mir::place::PlaceValue; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, ReturnSlot}; use rustc_data_structures::thin_vec::ThinVec; use rustc_middle::ty; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{Ty, TyCtxt, TypingEnv}; use rustc_span::bug; use rustc_target::callconv::PassMode; use tracing::debug; @@ -50,12 +50,8 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( if let ty::Slice(element_ty) = tail_ty.kind() { // Now we need to figure out the size of each slice element in memory to allow // safety checks and usability improvements in the backend. - let pci = PseudoCanonicalInput { - typing_env: TypingEnv::fully_monomorphized(), - value: *element_ty, - }; - let layout = tcx.layout_of(pci); + let layout = tcx.layout_of(typing_env.as_query_input(*element_ty)); let elem_size = match layout { Ok(layout) => layout.size, Err(_) => { @@ -91,9 +87,7 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } } - let pci = PseudoCanonicalInput { typing_env: TypingEnv::fully_monomorphized(), value: *ty }; - - let layout = match tcx.layout_of(pci) { + let layout = match tcx.layout_of(typing_env.as_query_input(*ty)) { Ok(layout) => layout.layout, Err(_) => { bug!("failed to compute layout for type {:?}", ty); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0e7547a0db9a4..bea61bd79f561 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -22,7 +22,7 @@ use rustc_middle::mir::BinOp; use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::offload_meta::OffloadMetadata; -use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt}; use rustc_session::diagnostics::feature_err; use rustc_span::{ErrorGuaranteed, Span, Symbol, bug, span_bug, sym}; use rustc_structures::CrateType; @@ -1886,7 +1886,7 @@ fn codegen_autodiff<'ll, 'tcx>( adjust_activity_to_abi( tcx, source_fn_ptr_ty, - TypingEnv::fully_monomorphized(), + bx.cx.typing_env(), &mut diff_attrs.input_activity, ); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f9dfd04faf2ac..e7bdb30c370dc 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -451,7 +451,7 @@ where let instance = match ty.kind() { &ty::FnDef(def_id, args) => Instance::expect_resolve( cx.tcx(), - ty::TypingEnv::fully_monomorphized(), + cx.typing_env(), def_id, args.no_bound_vars().unwrap(), expr.span, diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 8e3533deb5dfc..a6ad7282ca08d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -222,7 +222,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if tcx.features().ergonomic_clones() { let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions( tcx, - ty::TypingEnv::fully_monomorphized(), + cx.typing_env(), ty::EarlyBinder::bind(tcx, mir.clone()), ); mir = tcx.arena.alloc(optimize_use_clone::(cx, monomorphized_mir)); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 1ba7ce953b5d4..9b1f43156522e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -391,7 +391,7 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option { let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( "when UNSTABLE_RUSTDOC_TEST_PATH is set \ @@ -400,12 +400,13 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option() .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be a number"); - FileName::doc_test_source_code(PathBuf::from(path), line) + Some(Input::DocTestStr { file: PathBuf::from(path), line, input }) } - Err(_) => FileName::anon_source_code(&input), - }; - - Some(Input::Str { name, input }) + Err(_) => { + let name = FileName::anon_source_code(&input); + Some(Input::Str { name, input }) + } + } } [ifile] => Some(Input::File(PathBuf::from(ifile))), [ifile1, ifile2, ..] => early_dcx.early_fatal(format!( @@ -619,7 +620,7 @@ fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { } safe_println!("{}", String::from_utf8(v).unwrap()); } - Input::Str { .. } => { + Input::Str { .. } | Input::DocTestStr { .. } => { sess.dcx().fatal("cannot list metadata for stdin"); } } @@ -1373,12 +1374,14 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { Input::File(file) => { new_parser_from_file(&sess.psess, file, StripTokens::ShebangAndFrontmatter, None) } - Input::Str { name, input } => new_parser_from_source_str( - &sess.psess, - name.clone(), - input.clone(), - StripTokens::ShebangAndFrontmatter, - ), + instr @ (Input::Str { input, .. } | Input::DocTestStr { input, .. }) => { + new_parser_from_source_str( + &sess.psess, + instr.file_name(sess), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ) + } }); parser.parse_inner_attributes() } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 98155d9a8d5ef..fbe20c849de9c 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1867,7 +1867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Anything else would be completely captured and therefore handled already. - _ => unreachable!(), + _ => unreachable!("unexpected base_path_ty: {base_path_ty:?}",), } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8975f01de5216..c9788c3f920e3 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -61,12 +61,14 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { StripTokens::ShebangAndFrontmatter, None, ), - Input::Str { input, name } => new_parser_from_source_str( - &sess.psess, - name.clone(), - input.clone(), - StripTokens::ShebangAndFrontmatter, - ), + instr @ (Input::Str { input, .. } | Input::DocTestStr { input, .. }) => { + new_parser_from_source_str( + &sess.psess, + instr.file_name(sess), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ) + } }); parser.parse_crate_mod() }) diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 7fcabd423e778..c7837eeec307b 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -74,10 +74,14 @@ impl<'tcx> TyCtxt<'tcx> { ) { let value = self.erase_and_anonymize_regions(value); if value.has_aliases() { - assert_eq!( - value.clone(), - value.fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, typing_env }) - ) + // We want this check to also detect cases where an alias is incorrectly + // rigid in the current `TypingEnv`. This is the case if we use values + // which have been normalized in a different `TypingEnv` without + // renormalizing them. This was necessary to catch e.g. #163724. + let normalized = ty::set_aliases_to_non_rigid(self, value.clone()) + .skip_normalization() + .fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, typing_env }); + assert_eq!(value, normalized); } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 09c76d6beb4a1..18dea6dafd2dd 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -736,7 +736,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let typing_env = ty::TypingEnv::fully_monomorphized(); + let typing_env = self.typing_env(); let size = self.tcx.layout_of(typing_env.as_query_input(ty)).unwrap().size; let literal = Const::from_bits(self.tcx, size.unsigned_int_max(), typing_env, ty); @@ -746,7 +746,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Helper to get the minimum value of the appropriate type fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { assert!(ty.is_signed()); - let typing_env = ty::TypingEnv::fully_monomorphized(); + let typing_env = self.typing_env(); let bits = self.tcx.layout_of(typing_env.as_query_input(ty)).unwrap().size.bits(); let n = 1 << (bits - 1); let literal = Const::from_bits(self.tcx, n, typing_env, ty); diff --git a/compiler/rustc_mir_build/src/builder/misc.rs b/compiler/rustc_mir_build/src/builder/misc.rs index 6e8e74fd4fc8d..101d07cea2ce7 100644 --- a/compiler/rustc_mir_build/src/builder/misc.rs +++ b/compiler/rustc_mir_build/src/builder/misc.rs @@ -2,7 +2,7 @@ //! kind of thing. use rustc_middle::mir::*; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -32,7 +32,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Returns a zero literal operand for the appropriate type, works for /// bool, char and integers. pub(crate) fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = Const::from_bits(self.tcx, 0, ty::TypingEnv::fully_monomorphized(), ty); + let literal = Const::from_bits(self.tcx, 0, self.typing_env(), ty); self.literal_operand(span, literal) } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 39bd2be9409a3..bb0685cb7ff08 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -127,6 +127,8 @@ impl Direction for Backward { analysis.apply_primary_statement_effect(state, stmt, loc); vis.visit_after_primary_statement_effect(state, stmt, loc); } + + vis.visit_block_exit(state, block); } } @@ -242,5 +244,7 @@ impl Direction for Forward { vis.visit_after_early_terminator_effect(state, term, loc); analysis.apply_primary_terminator_effect(state, term, loc); vis.visit_after_primary_terminator_effect(state, term, loc); + + vis.visit_block_exit(state, block); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index e4b840a73e502..5b8a3374e04d9 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -34,6 +34,13 @@ pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { + /// Called after all effects in a block have been applied in the direction + /// of the analysis. + /// + /// In a forwards analysis, `state` is from the block's end. In a backwards + /// analysis, `state` is from the block's start. + fn visit_block_exit(&mut self, _state: &A::Domain, _block: BasicBlock) {} + /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( &mut self, diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 1e12e41ce1fb4..faa4b0dd1694b 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -1,6 +1,7 @@ mod borrowed_locals; mod initialized; mod liveness; +mod precise_liveness; mod storage_liveness; pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals}; @@ -11,6 +12,10 @@ pub use self::initialized::{ pub use self::liveness::{ DefUse, LivenessTransferFunction, MaybeLiveLocals, MaybeTransitiveLiveLocals, }; +pub use self::precise_liveness::{ + SplitPointEffect, SplitPointIndex, VisitAllocatedLocalsWith, VisitDeallocatedLocalsWith, + dump_liveness_matrix, liveness_matrix, +}; pub use self::storage_liveness::{ MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals, }; diff --git a/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs new file mode 100644 index 0000000000000..0234c240ef304 --- /dev/null +++ b/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs @@ -0,0 +1,596 @@ +//! Computes the points where each local must have a distinct allocation. +//! +//! The result is a [`SparseIntervalMatrix`] with one row per local. Two locals +//! may share the same address only if their rows are disjoint. To model MIR +//! statements where a source operand and destination place may share an +//! address, each statement and terminator is split into an early point, where +//! operands are read, and a late point, where destinations are written. +//! +//! A local live range starts at the late point of any statement or terminator +//! that writes to it without a `Deref` projection. It ends at `StorageLive` or +//! `StorageDead`, or at the early point of a whole-local move operand or the +//! last use of that local on a control-flow path (only for locals whose address +//! is never observed). This happens at `StorageLive` because it resets a local +//! to an uninitialized state. +//! +//! `Call` terminators are handled specially: move operands are kept live +//! through the late point of the terminator so they conflict with each other +//! and with the destination place. This matches the runtime behavior where the +//! place is donated to the callee for the duration of the call. +//! +//! The analysis consists of three passes: +//! +//! 1. A backward value-liveness analysis which identifies the last point at +//! which each local is directly used in each control flow path. This is used +//! to shorten the lifetime of unborrowed locals to their last use. +//! 2. A forward allocation-liveness analysis which uses that information +//! together with writes, moves, and storage markers to compute which +//! allocations may be live on entry to each block. +//! 3. A final walk using those entry states which constructs the matrix and +//! distinguishes whether each effect happens at the early or late point of a +//! statement or terminator. + +use rustc_index::IndexVec; +use rustc_index::bit_set::DenseBitSet; +use rustc_index::interval::SparseIntervalMatrix; +use rustc_middle::mir::visit::{ + MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, VisitPlacesWith, + Visitor, +}; +use rustc_middle::mir::{self, BasicBlock, Local, Location, MirDumper, PassWhere, Place}; +use rustc_middle::ty::TyCtxt; +use tracing::trace; + +use crate::impls::{DefUse, MaybeLiveLocals, borrowed_locals}; +use crate::points::{DenseLocationMap, PointIndex}; +use crate::{Analysis, GenKill, ResultsVisitor, visit_results}; + +/// Visits locals that are implicitly allocated by a statement or terminator as +/// part of destination place evaluation. +pub struct VisitAllocatedLocalsWith(pub F); + +impl<'tcx, F> Visitor<'tcx> for VisitAllocatedLocalsWith +where + F: FnMut(Local, PlaceContext), +{ + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _: Location) { + match DefUse::for_place(*place, context) { + DefUse::Def | DefUse::PartialWrite => (self.0)(place.local, context), + DefUse::Use | DefUse::NonUse => {} + } + } +} + +/// Visits locals that are implicitly freed by being evaluated as a bare-local +/// move operand or through a storage statement. +pub struct VisitDeallocatedLocalsWith(pub F); + +impl<'tcx, F> Visitor<'tcx> for VisitDeallocatedLocalsWith +where + F: FnMut(Local, PlaceContext), +{ + fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, location: Location) { + match statement.kind { + // StorageLive resets a local to the live-but-unallocated state, + // which also has the effect of freeing its allocation. + mir::StatementKind::StorageLive(local) => { + (self.0)(local, PlaceContext::NonUse(NonUseContext::StorageLive)); + } + mir::StatementKind::StorageDead(local) => { + (self.0)(local, PlaceContext::NonUse(NonUseContext::StorageDead)); + } + _ => self.super_statement(statement, location), + } + } + + fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, _: Location) { + if let mir::Operand::Move(place) = operand + && let Some(local) = place.as_local() + { + (self.0)(local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Backward dataflow pass +// +// This pass computes "kill points" for each local, indicating the location of +// their last use in a particular control flow branch. These are later used in +// the forward pass to end the live range of locals that are never borrowed at +// their last direct use. +// +// This pass ignores borrowed locals: we can't determine their last use from +// syntactic uses of the base local, so we conservatively treat them as always +// live in the backwards pass and don't emit kill points for them. The main +// forward pass still kills at `StorageDead` and whole-local moves, which is +// when their allocation is freed. +// +// This pass has 2 outputs: a set of kill points that mark the last use +// locations of locals and a per-block bitset indicating which locals are live +// on entry to that block. The latter acts as a set of kill points at block +// entry for all locals not in the bit set. + +struct KillPoints<'a> { + live_on_entry: IndexVec>, + kill_points_map: IndexVec, +} + +impl<'a> KillPoints<'a> { + fn compute<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + pass_name: Option<&'static str>, + points: &DenseLocationMap, + kill_points: &'a mut Vec<(Local, Location)>, + ) -> Self { + let maybe_live_locals = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, pass_name); + let borrowed_locals = borrowed_locals(body); + + // Initialize all borrowed locals as live on entry. We never try to kill + // those. + let mut live_on_entry = + IndexVec::from_elem_n(borrowed_locals.clone(), body.basic_blocks.len()); + + // Collect kill points and live-on-entry states from the results of + // MaybeLiveLocals. + kill_points.clear(); + let mut visitor = KillPointsVisitor { + kill_points, + live_on_entry: &mut live_on_entry, + borrowed_locals: &borrowed_locals, + }; + visit_results( + body, + mir::traversal::reachable(body).map(|(block, _)| block), + &maybe_live_locals, + &mut visitor, + ); + trace!(?kill_points); + trace!(?live_on_entry); + + // Create a mapping of `PointIndex` to the set of killed locals at that + // location. + let mut kill_points_map = IndexVec::from_elem_n(&[][..], points.num_points()); + for chunk in kill_points.chunk_by(|a, b| a.1 == b.1) { + let point = points.point_from_location(chunk[0].1); + trace!("Kill points at {:?}: {:?}", chunk[0].1, chunk); + debug_assert!(kill_points_map[point].is_empty()); + kill_points_map[point] = chunk; + } + + Self { live_on_entry, kill_points_map } + } +} + +struct KillPointsVisitor<'a> { + kill_points: &'a mut Vec<(Local, Location)>, + live_on_entry: &'a mut IndexVec>, + borrowed_locals: &'a DenseBitSet, +} + +impl<'tcx> ResultsVisitor<'tcx, MaybeLiveLocals> for KillPointsVisitor<'_> { + fn visit_block_exit(&mut self, state: &DenseBitSet, block: BasicBlock) { + // Borrowed locals are already marked as live when live_on_entry was + // initialized. This adds the non-borrowed locals that we have + // determined are live on entry to this block. + self.live_on_entry[block].union(state); + } + + fn visit_after_early_statement_effect( + &mut self, + state: &DenseBitSet, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + // Ignore non-uses. + match ctxt { + PlaceContext::NonMutatingUse(_) | PlaceContext::MutatingUse(_) => {} + PlaceContext::NonUse(_) => return, + } + + // If a local is used in a statement but is dead after it then this + // location is a kill point. Don't emit a kill point for borrowed + // locals. + if !state.contains(place.local) && !self.borrowed_locals.contains(place.local) { + self.kill_points.push((place.local, location)); + } + }) + .visit_statement(statement, location); + } + + fn visit_after_early_terminator_effect( + &mut self, + state: &DenseBitSet, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + // Ignore non-uses (they don't do anything) and edge uses + // (implicitly killed though live_on_entry at the start of the + // corresponding successor). + match ctxt { + PlaceContext::MutatingUse( + MutatingUseContext::AsmOutput + | MutatingUseContext::Call + | MutatingUseContext::Yield, + ) + | PlaceContext::NonUse(_) => return, + PlaceContext::NonMutatingUse(_) | PlaceContext::MutatingUse(_) => {} + } + + // If a local is used in a terminator but is dead after it then this + // location is a kill point. Don't emit a kill point for borrowed + // locals. + if !state.contains(place.local) && !self.borrowed_locals.contains(place.local) { + self.kill_points.push((place.local, location)); + } + }) + .visit_terminator(terminator, location); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Forward dataflow pass +// +// This is a simpler version of the main pass in `liveness_matrix`. It only +// needs to determine local liveness at block edges and doesn't distinguish +// between "early" and "late" effects. However it must still preserve the +// ordering of effects: moved operands are killed before destinations are +// generated. + +struct PreciseLiveness<'a> { + kill_points: &'a KillPoints<'a>, + points: &'a DenseLocationMap, +} + +impl PreciseLiveness<'_> { + fn apply_block_start_effect(&self, state: &mut DenseBitSet, block: BasicBlock) { + // Kill any unborrowed locals that are dead at the start of this block. + // Notably this kills any dead results produced by a predecessor's + // terminator. Borrowed locals are always included in live_on_entry. + state.intersect(&self.kill_points.live_on_entry[block]); + } +} + +impl<'tcx> Analysis<'tcx> for PreciseLiveness<'_> { + type Domain = DenseBitSet; + + const NAME: &'static str = "precise_liveness"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> DenseBitSet { + DenseBitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut DenseBitSet) { + // Function arguments start out as live. + for arg in body.args_iter() { + state.gen_(arg); + } + } + + fn apply_primary_statement_effect( + &self, + state: &mut DenseBitSet, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + if location.statement_index == 0 { + self.apply_block_start_effect(state, location.block); + } + + VisitDeallocatedLocalsWith(|local, _| state.kill(local)) + .visit_statement(statement, location); + VisitAllocatedLocalsWith(|local, _| state.gen_(local)).visit_statement(statement, location); + + // Apply kill points at this statement: if a variable is dead then it + // doesn't need storage. + let point = self.points.point_from_location(location); + for &(local, _) in self.kill_points.kill_points_map[point] { + state.kill(local); + } + } + + fn apply_primary_terminator_effect( + &self, + state: &mut DenseBitSet, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + if location.statement_index == 0 { + self.apply_block_start_effect(state, location.block); + } + + VisitDeallocatedLocalsWith(|local, _| state.kill(local)) + .visit_terminator(terminator, location); + + // Gen destination places. + VisitAllocatedLocalsWith(|local, ctxt| { + // The resume place of a Yield is only evaluated after the coroutine + // is resumed. This is in contrast to call destinations and inline + // asm outputs which are evaluated before the call and therefore + // remain allocated on both normal and unwind edges. + if ctxt != PlaceContext::MutatingUse(MutatingUseContext::Yield) { + state.gen_(local); + } + }) + .visit_terminator(terminator, location); + } + + fn apply_call_return_effect( + &self, + state: &mut DenseBitSet, + _block: BasicBlock, + return_places: mir::CallReturnPlaces<'_, 'tcx>, + ) { + if let mir::CallReturnPlaces::Yield(place) = return_places + && !place.is_indirect() + { + state.gen_(place.local); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Matrix construction + +/// Different "phases" of a single MIR statement, used to describe how +/// overlapping operands are handled. +/// +/// As a general rule, source operands are read in the `Early` phase and +/// destination places are written in the `Late` phase. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SplitPointEffect { + Early = 0, + Late = 1, +} + +rustc_index::newtype_index! { + /// A `PointIndex` with the lower bit encoding early/late inside a + /// statement. + /// + /// This is used to model overlap constraints within a MIR statement: if a + /// source/destination are allowed to overlap then the source is read in + /// `SplitPointEffect::Early` and the write is done in + /// `SplitPointEffect::Late`. + #[orderable] + #[debug_format = "SplitPointIndex({})"] + pub struct SplitPointIndex {} +} + +impl SplitPointIndex { + pub fn new(point: PointIndex, effect: SplitPointEffect) -> SplitPointIndex { + let index = (point.as_u32() << 1) | (effect as u32); + SplitPointIndex::from_u32(index) + } + + pub fn point(self) -> PointIndex { + PointIndex::from_u32(self.as_u32() >> 1) + } + + pub fn effect(self) -> SplitPointEffect { + match self.as_u32() & 1 { + 0 => SplitPointEffect::Early, + 1 => SplitPointEffect::Late, + _ => unreachable!(), + } + } +} + +/// Helper type to construct a `SparseIntervalMatrix`. +struct MatrixBuilder { + matrix: SparseIntervalMatrix, + range_start: IndexVec>, + + // Track locals that have been live at any point in a block so that at the + // end of a block we don't need to iterate over all locals. This + // significantly speeds up matrix building. + maybe_live_locals: Vec, +} + +impl MatrixBuilder { + fn gen_(&mut self, local: Local, point: PointIndex, effect: SplitPointEffect) { + let split_point = SplitPointIndex::new(point, effect); + + // No-op if the local is already live. + if self.range_start[local].is_none() { + self.range_start[local] = Some(split_point); + self.maybe_live_locals.push(local); + } + } + + fn kill(&mut self, local: Local, point: PointIndex, effect: SplitPointEffect) { + let end = SplitPointIndex::new(point, effect); + + // No-op if the local is already dead. + if let Some(start) = self.range_start[local].take() { + debug_assert!(end >= start); + self.matrix.append_range(local, start..=end); + } + } + + fn kill_all(&mut self, point: PointIndex, effect: SplitPointEffect) { + while let Some(local) = self.maybe_live_locals.pop() { + self.kill(local, point, effect); + } + } + + fn kill_all_except(&mut self, except: Local, point: PointIndex, effect: SplitPointEffect) { + while let Some(local) = self.maybe_live_locals.pop() { + if local != except { + self.kill(local, point, effect); + } + } + self.maybe_live_locals.push(except); + } +} + +pub fn liveness_matrix<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + points: &DenseLocationMap, + pass_name: Option<&'static str>, +) -> SparseIntervalMatrix { + let mut kill_points_vec = vec![]; + let kill_points = KillPoints::compute(tcx, body, pass_name, points, &mut kill_points_vec); + let mut results = PreciseLiveness { kill_points: &kill_points, points } + .iterate_to_fixpoint(tcx, body, pass_name); + + let mut builder = MatrixBuilder { + matrix: SparseIntervalMatrix::new(points.num_points() * 2), + range_start: IndexVec::from_elem_n(None, body.local_decls.len()), + maybe_live_locals: Vec::new(), + }; + for (block, block_data) in body.basic_blocks.iter_enumerated() { + // We can mutate the state in-place since we're not using it any more + // after this point. + let state = &mut results.entry_states[block]; + + // Notably this kills any dead results produced by a predecessor's + // terminator. + state.intersect(&kill_points.live_on_entry[block]); + + // Gen any locals that are live at the start of the block. If this block + // only consists of a return terminator then only gen the return place + // instead. This ensures that StorageDead for all other locals are + // inserted before the return terminator. + let terminator = block_data.terminator(); + if let mir::TerminatorKind::Return = terminator.kind + && block_data.statements.is_empty() + { + if state.contains(mir::RETURN_PLACE) { + builder.gen_(mir::RETURN_PLACE, points.entry_point(block), SplitPointEffect::Early); + } + } else { + for local in state.iter() { + builder.gen_(local, points.entry_point(block), SplitPointEffect::Early); + } + } + + for (statement_index, statement) in block_data.statements.iter().enumerate() { + let location = Location { block, statement_index }; + let point = points.point_from_location(location); + + // Kill moved locals at the early point. + VisitDeallocatedLocalsWith(|local, _| { + builder.kill(local, point, SplitPointEffect::Early); + }) + .visit_statement(statement, location); + + // Kill any locals which are no longer used after this statement. + for &(local, _) in kill_points.kill_points_map[point] { + builder.kill(local, point, SplitPointEffect::Early); + } + + // Gen destination places at the late point. + VisitAllocatedLocalsWith(|local, _| { + builder.gen_(local, point, SplitPointEffect::Late); + }) + .visit_statement(statement, location); + + // Kill any dead destination places: they will only appear at the + // late point of the statement they are generated in, which is + // sufficient for determining overlap. + for &(local, _) in kill_points.kill_points_map[point] { + builder.kill(local, point, SplitPointEffect::Late); + } + } + + // If this block ends in a return terminator, end all live ranges before + // the terminator so that StorageDead statements are inserted before it. + // + // This is useful after inlining so that the lifetime of locals in the + // inlined callee don't extend past the call in the caller. + if let mir::TerminatorKind::Return = terminator.kind + && !block_data.statements.is_empty() + { + // Blocks with only a return terminator are handled above. + let location = Location { block, statement_index: block_data.statements.len() - 1 }; + let point = points.point_from_location(location); + builder.kill_all_except(mir::RETURN_PLACE, point, SplitPointEffect::Late); + } + + let location = Location { block, statement_index: block_data.statements.len() }; + let point = points.point_from_location(location); + + // Kill moved locals at the early point. + VisitDeallocatedLocalsWith(|local, _| { + builder.kill(local, point, SplitPointEffect::Early); + }) + .visit_terminator(terminator, location); + + // Kill any locals which are no longer used after this terminator. + for &(local, _) in kill_points.kill_points_map[point] { + builder.kill(local, point, SplitPointEffect::Early); + } + + // Gen destination places at the late point. + VisitAllocatedLocalsWith(|local, _| { + builder.gen_(local, point, SplitPointEffect::Late); + }) + .visit_terminator(terminator, location); + + // Move arguments to a call are treated specially: the place that they + // represent is passed directly to the callee, which means that they are + // not allowed to alias any other move operand or the destination place. + // This is represented here by extending their live range to the late + // part, making it overlap with that of the destination place. + // + // Notably, this *doesn't* apply to TailCall. + if let mir::TerminatorKind::Call { + func: _, + args, + destination: _, + target: _, + unwind: _, + call_source: _, + fn_span: _, + } = &terminator.kind + { + for arg in args { + if let mir::Operand::Move(place) = arg.node + && !place.is_indirect() + { + builder.gen_(place.local, point, SplitPointEffect::Late); + } + } + } + + // End the lifetimes of all locals at the end of the block. Successor + // blocks (which may not be contiguous in the index space!) will + // initialize the lifetimes again from their entry state. + builder.kill_all(point, SplitPointEffect::Late); + } + + builder.matrix +} + +pub fn dump_liveness_matrix<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + pass_name: &'static str, + points: &DenseLocationMap, + matrix: &SparseIntervalMatrix, +) { + let locals_live_at = |split_point| { + matrix.rows().filter(|&r| matrix.contains(r, split_point)).collect::>() + }; + + if let Some(dumper) = MirDumper::new(tcx, pass_name, body) { + let extra_data = &|pass_where, w: &mut dyn std::io::Write| { + if let PassWhere::BeforeLocation(loc) = pass_where { + let point = points.point_from_location(loc); + let split_point = SplitPointIndex::new(point, SplitPointEffect::Early); + let live = locals_live_at(split_point); + writeln!(w, " // {loc:?}-early => {live:?}")?; + let split_point = SplitPointIndex::new(point, SplitPointEffect::Late); + let live = locals_live_at(split_point); + writeln!(w, " // {loc:?}-late => {live:?}")?; + } + Ok(()) + }; + + dumper.set_extra_data(extra_data).dump_mir(body) + } +} diff --git a/compiler/rustc_mir_dataflow/src/pretty.rs b/compiler/rustc_mir_dataflow/src/pretty.rs index b37ad708e20e0..5a159ae40bd3c 100644 --- a/compiler/rustc_mir_dataflow/src/pretty.rs +++ b/compiler/rustc_mir_dataflow/src/pretty.rs @@ -7,7 +7,11 @@ use rustc_middle::ty::TyCtxt; use crate::debuginfo::debuginfo_locals; use crate::framework::Analysis; -use crate::impls::{MaybeLiveLocals, MaybeTransitiveLiveLocals, borrowed_locals}; +use crate::impls::{ + MaybeLiveLocals, MaybeTransitiveLiveLocals, SplitPointEffect, SplitPointIndex, borrowed_locals, + liveness_matrix, +}; +use crate::points::DenseLocationMap; use crate::{ResultsVisitor, visit_results}; type ExtraDataFn = dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>; @@ -33,6 +37,29 @@ pub(crate) fn mir_pretty_extra_data<'tcx>( &mut Annotator { annotations: &mut annotations }, ); } + RustcMirKind::PrettyPreciseLiveness => { + let points = DenseLocationMap::new(body); + let matrix = liveness_matrix(tcx, body, &points, None); + for (block, data) in body.basic_blocks.iter_enumerated() { + for statement_index in 0..=data.statements.len() { + let location = mir::Location { block, statement_index }; + let point = points.point_from_location(location); + let locals_live_at = |effect| { + let split_point = SplitPointIndex::new(point, effect); + matrix + .rows() + .filter(|&r| matrix.contains(r, split_point)) + .collect::>() + }; + let early = locals_live_at(SplitPointEffect::Early); + annotations + .add(PassWhere::BeforeLocation(location), format!("early: {early:?}")); + let late = locals_live_at(SplitPointEffect::Late); + annotations + .add(PassWhere::BeforeLocation(location), format!("late: {late:?}")); + } + } + } RustcMirKind::PrettyTransitiveLiveLocals => { let borrowed_locals = borrowed_locals(body); let debuginfo_locals = debuginfo_locals(body); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9b7e8e38ed788..a65af3c6788b3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -463,14 +463,14 @@ impl<'a> Parser<'a> { /// 2. tuple type /// 3. bare trait object type where the first trait bound is parenthesized fn parse_paren_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { - let mut trailing_plus = false; - let (ts, trailing) = self.parse_paren_comma_seq(|p| { + let mut inside_has_trailing_plus = false; + let (ts, trailing_comma) = self.parse_paren_comma_seq(|p| { let ty = p.parse_ty()?; - trailing_plus = p.prev_token == TokenKind::Plus; + inside_has_trailing_plus = p.prev_token == TokenKind::Plus; Ok(ty) })?; - if ts.len() == 1 && matches!(trailing, Trailing::No) { + if ts.len() == 1 && matches!(trailing_comma, Trailing::No) { let ty = ts.into_iter().next().unwrap(); // Let's check if we actually have a bare trait object type where the first trait bound @@ -478,11 +478,12 @@ impl<'a> Parser<'a> { // what's contained between the parentheses resembles a *BareTraitBound*. // // For context, looking at bounds in general (see *Bound*), only trait bounds are - // allowed to be wrapped in parentheses, not however lifetime and use bounds. - let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus(); + // allowed to be wrapped in parentheses, not however outlives and use bounds. + let outside_has_eligible_trailing_plus = + allow_plus == AllowPlus::Yes && self.token.is_like_plus(); match ty.kind { // `"(" TypePath ")" "+"` - TyKind::Path(None, path) if maybe_bounds => self + TyKind::Path(None, path) if outside_has_eligible_trailing_plus => self .finish_parsing_bare_trait_object_ty( ThinVec::new(), path, @@ -490,18 +491,18 @@ impl<'a> Parser<'a> { true, ast::Parens::Yes, ), - // `"(" BareTraitBound\TypePath | UseBound ")" "+"` + // `"(" BareTraitBound\TypePath ")" "+"` // - // * FIXME: As alluded to above, only trait bounds are meant to allow parens. - // Arguably, it's an accident that we're permitting *UseBound*s and thus types - // like `(use<>)+`. Might need a T-lang FCP to change this. - // * We're checking `!trailing_plus` to prevent us from accepting code like - // `(T+)+` or `('a+)+`. - // * While we could be looking at `('a)+` which we don't want to accept, we - // know that the `parse_ty` above has already emitted an error since the - // lifetime isn't immediately followed by a `+`. + // We actually accept outlives bounds here, too, purely to reduce diagnostic output + // for ill-formed code like `('a)+`: We know for a fact that the `parse_ty` above + // has already emitted an error for the inner `'a` as it's not followed by `+`. + // This way we indirectly suppress unhelpful follow-up diagnostics like E0178 + // ("bad `+` in type") and E0224 ("no trait bound in trait object type"). TyKind::TraitObject(mut bounds, TraitObjectSyntax::None) - if maybe_bounds && bounds.len() == 1 && !trailing_plus => + if outside_has_eligible_trailing_plus + && !inside_has_trailing_plus + && let [ast::GenericBound::Trait(_) | ast::GenericBound::Outlives(_)] = + bounds.as_slice() => { self.eat_plus(); bounds.append(&mut self.parse_generic_bounds()?); diff --git a/compiler/rustc_public_bridge/src/alloc.rs b/compiler/rustc_public_bridge/src/alloc.rs index 7e6af3425465a..675f0f4cf8671 100644 --- a/compiler/rustc_public_bridge/src/alloc.rs +++ b/compiler/rustc_public_bridge/src/alloc.rs @@ -8,6 +8,7 @@ use rustc_abi::{Size, TyAndLayout}; use rustc_middle::mir::interpret::{ AllocId, AllocInit, AllocRange, Allocation, ConstAllocation, Pointer, Scalar, alloc_range, }; +use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::{Ty, layout}; use super::{CompilerCtxt, Tables}; @@ -18,8 +19,7 @@ pub fn create_ty_and_layout<'tcx, B: Bridge>( cx: &CompilerCtxt<'tcx, B>, ty: Ty<'tcx>, ) -> Result>, &'tcx layout::LayoutError<'tcx>> { - use crate::context::TypingEnvHelpers; - cx.tcx.layout_of(cx.fully_monomorphized().as_query_input(ty)) + cx.tcx.layout_of(cx.typing_env().as_query_input(ty)) } pub fn try_new_scalar<'tcx, B: Bridge>( diff --git a/compiler/rustc_public_bridge/src/context/helpers.rs b/compiler/rustc_public_bridge/src/context/helpers.rs index 21eef29e5f1cc..e5a3aa0457612 100644 --- a/compiler/rustc_public_bridge/src/context/helpers.rs +++ b/compiler/rustc_public_bridge/src/context/helpers.rs @@ -4,7 +4,6 @@ //! interface regardless of internal compiler changes. use rustc_middle::mir::interpret::AllocRange; -use rustc_middle::ty; use rustc_middle::ty::Ty; use rustc_span::def_id::DefId; @@ -12,10 +11,6 @@ pub trait TyHelpers<'tcx> { fn new_foreign(&self, def_id: DefId) -> Ty<'tcx>; } -pub trait TypingEnvHelpers<'tcx> { - fn fully_monomorphized(&self) -> ty::TypingEnv<'tcx>; -} - pub trait AllocRangeHelpers<'tcx> { fn alloc_range(&self, offset: rustc_abi::Size, size: rustc_abi::Size) -> AllocRange; } diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index f8ab16a54b451..742e863749518 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -12,7 +12,7 @@ use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::{BinOp, Body, Const as MirConst, ConstValue, UnOp}; use rustc_middle::ty::consts::ConstExt; -use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; +use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::print::{ with_forced_trimmed_paths, with_no_trimmed_paths, with_resolve_crate_name, }; @@ -28,7 +28,7 @@ use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_span::{Span, Symbol}; use rustc_target::callconv::FnAbi; -use super::{AllocRangeHelpers, CompilerCtxt, TyHelpers, TypingEnvHelpers}; +use super::{AllocRangeHelpers, CompilerCtxt, TyHelpers}; use crate::builder::BodyBuilder; use crate::{Bridge, Error, Tables, filter_def_ids}; @@ -38,12 +38,6 @@ impl<'tcx, B: Bridge> TyHelpers<'tcx> for CompilerCtxt<'tcx, B> { } } -impl<'tcx, B: Bridge> TypingEnvHelpers<'tcx> for CompilerCtxt<'tcx, B> { - fn fully_monomorphized(&self) -> ty::TypingEnv<'tcx> { - ty::TypingEnv::fully_monomorphized() - } -} - impl<'tcx, B: Bridge> AllocRangeHelpers<'tcx> for CompilerCtxt<'tcx, B> { fn alloc_range( &self, @@ -458,8 +452,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { /// Evaluate constant as a target usize. pub fn eval_target_usize(&self, cnst: MirConst<'tcx>) -> Result { - use crate::context::TypingEnvHelpers; - cnst.try_eval_target_usize(self.tcx, self.fully_monomorphized()) + cnst.try_eval_target_usize(self.tcx, self.typing_env()) .ok_or_else(|| B::Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } @@ -471,7 +464,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { pub fn try_new_const_zst(&self, ty_internal: Ty<'tcx>) -> Result, B::Error> { let size = self .tcx - .layout_of(self.fully_monomorphized().as_query_input(ty_internal)) + .layout_of(self.typing_env().as_query_input(ty_internal)) .map_err(|err| { B::Error::new(format!( "Cannot create a zero-sized constant for type `{ty_internal}`: {err}" @@ -523,11 +516,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { value: u128, ty_internal: Ty<'tcx>, ) -> Result, B::Error> { - let size = self - .tcx - .layout_of(self.fully_monomorphized().as_query_input(ty_internal)) - .unwrap() - .size; + let size = self.tcx.layout_of(self.typing_env().as_query_input(ty_internal)).unwrap().size; let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { B::Error::new(format!("Value overflow: cannot convert `{value}` to `{ty_internal}`.")) })?; @@ -539,11 +528,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { value: u128, ty_internal: Ty<'tcx>, ) -> Result, B::Error> { - let size = self - .tcx - .layout_of(self.fully_monomorphized().as_query_input(ty_internal)) - .unwrap() - .size; + let size = self.tcx.layout_of(self.typing_env().as_query_input(ty_internal)).unwrap().size; let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { B::Error::new(format!("Value overflow: cannot convert `{value}` to `{ty_internal}`.")) })?; @@ -585,11 +570,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { /// Returns the type of given definition instantiated with the given arguments. pub fn def_ty_with_args(&self, item: DefId, args_ref: GenericArgsRef<'tcx>) -> Ty<'tcx> { let def_ty = self.tcx.type_of(item); - self.tcx.instantiate_and_normalize_erasing_regions( - args_ref, - self.fully_monomorphized(), - def_ty, - ) + self.tcx.instantiate_and_normalize_erasing_regions(args_ref, self.typing_env(), def_ty) } /// `Span` of a `DefId`. @@ -625,7 +606,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { /// Get the instance type with generic instantiations applied and lifetimes erased. pub fn instance_ty(&self, instance: ty::Instance<'tcx>) -> Ty<'tcx> { assert!(!instance.has_non_region_param(), "{instance:?} needs further instantiation"); - instance.ty(self.tcx, self.fully_monomorphized()) + instance.ty(self.tcx, self.typing_env()) } /// Get the instantiation types. @@ -694,7 +675,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { def_id: DefId, args_ref: GenericArgsRef<'tcx>, ) -> Option> { - match Instance::try_resolve(self.tcx, self.fully_monomorphized(), def_id, args_ref) { + match Instance::try_resolve(self.tcx, self.typing_env(), def_id, args_ref) { Ok(Some(instance)) => Some(instance), Ok(None) | Err(_) => None, } @@ -712,7 +693,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { def_id: DefId, args_ref: GenericArgsRef<'tcx>, ) -> Option> { - Instance::resolve_for_fn_ptr(self.tcx, self.fully_monomorphized(), def_id, args_ref) + Instance::resolve_for_fn_ptr(self.tcx, self.typing_env(), def_id, args_ref) } /// Resolve instance for a closure with the requested type. @@ -728,7 +709,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { /// Try to evaluate an instance into a constant. pub fn eval_instance(&self, instance: ty::Instance<'tcx>) -> Result { self.tcx.const_eval_instance( - self.fully_monomorphized(), + self.typing_env(), instance, self.tcx.def_span(instance.def_id()), ) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 64cfdfd5f85a7..7e274c66f6f75 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1062,6 +1062,15 @@ impl Default for NextSolverConfig { pub enum Input { /// Load source code from a file. File(PathBuf), + /// Load source code from a doctest string. + DocTestStr { + /// The file associated with this input. + file: PathBuf, + // Line associated with this input + line: isize, + /// An anonymous string containing the source code. + input: String, + }, /// Load source code from a string. Str { /// A string that is shown in place of a filename. @@ -1092,6 +1101,14 @@ impl Input { .path_mapping() .to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()), ), + Input::DocTestStr { ref file, line, .. } => FileName::DocTest( + session + .psess + .source_map() + .path_mapping() + .to_real_filename(session.psess.source_map().working_dir(), file.as_path()), + line, + ), Input::Str { ref name, .. } => name.clone(), } } @@ -1099,6 +1116,7 @@ impl Input { pub fn opt_path(&self) -> Option<&Path> { match self { Input::File(file) => Some(file), + Input::DocTestStr { file, .. } => Some(file), Input::Str { name, .. } => match name { FileName::Real(real) => real.local_path(), FileName::CfgSpec(_) => None, @@ -1107,7 +1125,7 @@ impl Input { FileName::ProcMacroSourceCode(_) => None, FileName::CliCrateAttr(_) => None, FileName::Custom(_) => None, - FileName::DocTest(path, _) => Some(path), + FileName::DocTest(path, _) => path.local_path(), FileName::InlineAsm(_) => None, }, } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d48ee4fce6f8e..f048fb04af45a 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -520,7 +520,8 @@ pub enum FileName { CliCrateAttr(Hash64), /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), - DocTest(PathBuf, isize), + /// Custom path for doctests + DocTest(RealFileName, isize), /// Post-substitution inline assembly from LLVM. InlineAsm(Hash64), } @@ -543,7 +544,7 @@ impl fmt::Display for FileNameDisplay<'_> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use FileName::*; match *self.inner { - Real(ref name) => { + Real(ref name) | DocTest(ref name, _) => { write!(fmt, "{}", name.to_string_lossy(self.display_pref)) } CfgSpec(_) => write!(fmt, ""), @@ -552,7 +553,6 @@ impl fmt::Display for FileNameDisplay<'_> { ProcMacroSourceCode(_) => write!(fmt, ""), CliCrateAttr(_) => write!(fmt, ""), Custom(ref s) => write!(fmt, "<{s}>"), - DocTest(ref path, _) => write!(fmt, "{}", path.display()), InlineAsm(_) => write!(fmt, ""), } } @@ -643,7 +643,7 @@ impl FileName { FileName::CliCrateAttr(hasher.finish()) } - pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { + pub fn doc_test_source_code(path: RealFileName, line: isize) -> FileName { FileName::DocTest(path, line) } @@ -660,7 +660,7 @@ impl FileName { pub fn into_local_path(self) -> Option { match self { FileName::Real(path) => path.into_local_path(), - FileName::DocTest(path, _) => Some(path), + FileName::DocTest(path, _) => path.into_local_path(), _ => None, } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b9e7a9f5f94cb..a9275f489308a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1903,6 +1903,7 @@ symbols! { rustc_peek_maybe_uninit, rustc_preserve_ub_checks, rustc_pretty_live_locals, + rustc_pretty_precise_liveness, rustc_pretty_transitive_live_locals, rustc_private, rustc_proc_macro_decls, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e0450de6ab26b..3e0796bd1f585 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -520,27 +520,24 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn emit_next_solver_overflow_fcw(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>, span: Span) { let tcx = self.tcx; - let goal = self.deeply_resolve_ignoring_regions(goal); - let mut visitor = OverflowedGoalChain { - span, - predicates: vec![], - recursion_limit: usize::min(16, tcx.recursion_limit().0), - }; - - // HACK: avoid computing goal chains for dependencies by relying on the fact that - // `cargo` passes `lint_cap=allow` to deps. This should mitigate some of the perf/rss - // regression when compiling crates whose deps trigger a large number of these FCWs. - if !matches!(tcx.sess.opts.lint_cap, Some(rustc_lint_defs::Level::Allow)) { - let _ = self.with_disabled_next_solver_overflow_fcw(|| { - self.visit_proof_tree(goal, &mut visitor) - }); - } - tcx.emit_node_span_lint( RECURSION_DEPTH_EXCEEDING_LIMIT, CRATE_HIR_ID, span, rustc_errors::DiagDecorator(|diag| { + // We do this in the decorator as that avoids the work + // if we don't actually emit the lint. + let goal = self.deeply_resolve_ignoring_regions(goal); + let mut visitor = OverflowedGoalChain { + span, + predicates: vec![], + recursion_limit: usize::min(16, tcx.recursion_limit().0), + }; + + let _ = self.with_disabled_next_solver_overflow_fcw(|| { + self.visit_proof_tree(goal, &mut visitor) + }); + // FIXME: share this with overflow error in fulfillment instead of duplicating. let pred_str = |pred: ty::Predicate<'tcx>| { let s = pred.to_string(); diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index ad3fbce51d1d2..58691539edfa3 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/161049 +Last change is for: https://github.com/rust-lang/rust/pull/163954 diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 5dae391cc4bee..4ac0f6ea85679 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1724,7 +1724,6 @@ impl CommandLineStep for Lld { if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) && llvm_output.link_shared() - && target.contains("linux") { // So we inform LLD where it can find LLVM's libraries by adding an rpath entry to the // expected parent `lib` directory. @@ -1732,7 +1731,12 @@ impl CommandLineStep for Lld { // Be careful when changing this path, we need to ensure it's quoted or escaped: // `$ORIGIN` would otherwise be expanded when the `LdFlags` are passed verbatim to // cmake. - ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); + if target.contains("linux") { + ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); + } + if target.contains("apple-darwin") { + ldflags.push_all("-Wl,-rpath,'@loader_path/../../../'"); + } } configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]); diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index ebf488fcb31b5..7604973739f08 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -129,8 +129,10 @@ pub(crate) struct Options { pub(crate) no_run: bool, /// What sources are being mapped. pub(crate) remap_path_prefix: Vec<(PathBuf, PathBuf)>, + pub(crate) remap_path_prefix_strs: Vec, /// Which scope(s) to use with `--remap-path-prefix` pub(crate) remap_path_scope: RemapPathScopeComponents, + pub(crate) remap_path_scope_str: Option, /// The path to a rustc-like binary to build tests with. If not set, we /// default to loading from `$sysroot/bin/rustc`. @@ -857,6 +859,8 @@ impl Options { let unstable_opts_strs = matches.opt_strs("Z"); let lib_strs = matches.opt_strs("L"); let extern_strs = matches.opt_strs("extern"); + let remap_path_prefix_strs = matches.opt_strs("remap-path-prefix"); + let remap_path_scope_str = matches.opt_str("remap-path-scope"); let test_runtool = matches.opt_str("test-runtool"); let test_runtool_args = matches.opt_strs("test-runtool-arg"); let document_private = matches.opt_present("document-private-items"); @@ -932,7 +936,9 @@ impl Options { no_run, test_builder_wrappers, remap_path_prefix, + remap_path_prefix_strs, remap_path_scope, + remap_path_scope_str, no_capture, crate_name, output_format, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c8d1cfd599909..35b2af5370fff 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -103,6 +103,13 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> content.push(format!("--check-cfg={check_cfg}")); } + for prefix in &options.remap_path_prefix_strs { + content.push(format!("--remap-path-prefix={prefix}")); + } + if let Some(scope) = &options.remap_path_scope_str { + content.push(format!("--remap-path-scope={scope}")); + } + for lib_str in &options.lib_strs { content.push(format!("-L{lib_str}")); } @@ -986,7 +993,7 @@ impl ScrapedDocTest { } let name = format!( "{} - {item_path}(line {line})", - filename.display(RemapPathScopeComponents::DOCUMENTATION) + filename.display(RemapPathScopeComponents::DIAGNOSTICS) ); Self { filename, line, langstr, text, name, span, code_mappings, global_crate_attrs } @@ -999,13 +1006,12 @@ impl ScrapedDocTest { self.langstr.no_run || opts.no_run } - fn path(&self) -> PathBuf { - match &self.filename { - FileName::Real(name) => { - name.path(RemapPathScopeComponents::DOCUMENTATION).to_path_buf() - } - _ => PathBuf::from(r"doctest.rs"), - } + fn path(&self, scope: RemapPathScopeComponents) -> String { + self.filename.display(scope).to_string() + } + + fn local_path(&self) -> PathBuf { + self.filename.clone().into_local_path().unwrap_or_else(|| PathBuf::from(r"doctest.rs")) } } @@ -1138,7 +1144,7 @@ fn generate_test_desc_and_fn( ) -> test::TestDescAndFn { let target_str = rustdoc_options.target.to_string(); let rustdoc_test_options = - IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.path()); + IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.local_path()); debug!("creating test {}: {}", scraped_test.name, scraped_test.text); test::TestDescAndFn { diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a994f1d974ae3..05296b791709d 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -94,7 +94,7 @@ pub(crate) fn test(input: &Input, options: Options, dcx: DiagCtxtHandle<'_>) -> Input::File(path) => { read_to_string(path).map_err(|err| format!("{}: {err}", path.display()))? } - Input::Str { name: _, input } => input.clone(), + Input::Str { name: _, input } | Input::DocTestStr { input, .. } => input.clone(), }; // Obviously not a real crate name, but close enough for purposes of doctests. diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 43ed48aad95d4..3e4b4209f4145 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use std::time::Duration; use rustc_data_structures::fx::FxIndexSet; +use rustc_span::RemapPathScopeComponents; use rustc_span::edition::Edition; use crate::doctest::{ @@ -264,7 +265,7 @@ test::StaticTestFn( )); }}", test_name = scraped_test.name, - file = scraped_test.path(), + file = scraped_test.path(RemapPathScopeComponents::MACRO), line = scraped_test.line, no_run = scraped_test.langstr.no_run, should_panic = !scraped_test.langstr.no_run && scraped_test.langstr.should_panic, diff --git a/tests/mir-opt/precise_liveness.borrowed.dataflow.0.mir b/tests/mir-opt/precise_liveness.borrowed.dataflow.0.mir new file mode 100644 index 0000000000000..64f9513b8757a --- /dev/null +++ b/tests/mir-opt/precise_liveness.borrowed.dataflow.0.mir @@ -0,0 +1,30 @@ +// MIR for `borrowed` 0 dataflow + +fn borrowed() -> u32 { + let mut _0: u32; + let mut _1: u32; + let mut _2: u32; + let mut _3: *const u32; + let mut _4: *const u32; + + bb0: { + // early: [] + // late: [_1] + _1 = const 1_u32; + // early: [_1] + // late: [_1, _2] + _2 = const 2_u32; + // early: [_1, _2] + // late: [_1, _2, _3] + _3 = &raw const _1; + // early: [_1, _2] + // late: [_1, _2, _4] + _4 = &raw const _2; + // early: [_1, _2] + // late: [_0, _1] + _0 = Add(copy _1, move _2); + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.branch.dataflow.0.mir b/tests/mir-opt/precise_liveness.branch.dataflow.0.mir new file mode 100644 index 0000000000000..d92d560b0dab3 --- /dev/null +++ b/tests/mir-opt/precise_liveness.branch.dataflow.0.mir @@ -0,0 +1,33 @@ +// MIR for `branch` 0 dataflow + +fn branch(_1: bool) -> u32 { + let mut _0: u32; + let mut _2: u32; + + bb0: { + // early: [_1] + // late: [_1, _2] + _2 = const 1_u32; + // early: [_1, _2] + // late: [_2] + switchInt(copy _1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + // early: [_2] + // late: [_0] + _0 = move _2; + // early: [_0] + // late: [] + return; + } + + bb2: { + // early: [] + // late: [_0] + _0 = const 0_u32; + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.call.dataflow.0.mir b/tests/mir-opt/precise_liveness.call.dataflow.0.mir new file mode 100644 index 0000000000000..f5e6d06e672d4 --- /dev/null +++ b/tests/mir-opt/precise_liveness.call.dataflow.0.mir @@ -0,0 +1,39 @@ +// MIR for `call` 0 dataflow + +fn call() -> u32 { + let mut _0: u32; + let mut _1: u32; + let mut _2: u32; + let mut _3: u32; + let mut _4: *const u32; + + bb0: { + // early: [] + // late: [_1] + _1 = const 1_u32; + // early: [_1] + // late: [_1, _2] + _2 = const 2_u32; + // early: [_1, _2] + // late: [_1, _3] + _3 = callee(move _1, copy _2) -> [return: bb1, unwind: bb2]; + } + + bb1: { + // early: [_3] + // late: [_3, _4] + _4 = &raw const _3; + // early: [_3] + // late: [_0] + _0 = move _3; + // early: [_0] + // late: [] + return; + } + + bb2 (cleanup): { + // early: [_3] + // late: [_3] + resume; + } +} diff --git a/tests/mir-opt/precise_liveness.call_borrowed_field.dataflow.0.mir b/tests/mir-opt/precise_liveness.call_borrowed_field.dataflow.0.mir new file mode 100644 index 0000000000000..96ecf63a27f53 --- /dev/null +++ b/tests/mir-opt/precise_liveness.call_borrowed_field.dataflow.0.mir @@ -0,0 +1,35 @@ +// MIR for `call_borrowed_field` 0 dataflow + +fn call_borrowed_field() -> u32 { + let mut _0: u32; + let mut _1: (u32,); + let mut _2: u32; + let mut _3: *const (u32,); + + bb0: { + // early: [] + // late: [_1] + (_1.0: u32) = const 1_u32; + // early: [_1] + // late: [_1, _3] + _3 = &raw const _1; + // early: [_1] + // late: [_1, _2] + _2 = callee(move (_1.0: u32), const 0_u32) -> [return: bb1, unwind: bb2]; + } + + bb1: { + // early: [_1, _2] + // late: [_0, _1] + _0 = move _2; + // early: [_0] + // late: [] + return; + } + + bb2 (cleanup): { + // early: [_1] + // late: [_1] + resume; + } +} diff --git a/tests/mir-opt/precise_liveness.call_field.dataflow.0.mir b/tests/mir-opt/precise_liveness.call_field.dataflow.0.mir new file mode 100644 index 0000000000000..2bcd1434eeafd --- /dev/null +++ b/tests/mir-opt/precise_liveness.call_field.dataflow.0.mir @@ -0,0 +1,21 @@ +// MIR for `call_field` 0 dataflow + +fn call_field() -> u32 { + let mut _0: u32; + let mut _1: (u32,); + + bb0: { + // early: [] + // late: [_1] + (_1.0: u32) = const 1_u32; + // early: [_1] + // late: [_0, _1] + _0 = callee(move (_1.0: u32), const 0_u32) -> [return: bb1, unwind unreachable]; + } + + bb1: { + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.call_indirect.dataflow.0.mir b/tests/mir-opt/precise_liveness.call_indirect.dataflow.0.mir new file mode 100644 index 0000000000000..fff5e24358bda --- /dev/null +++ b/tests/mir-opt/precise_liveness.call_indirect.dataflow.0.mir @@ -0,0 +1,17 @@ +// MIR for `call_indirect` 0 dataflow + +fn call_indirect(_1: *const u32) -> u32 { + let mut _0: u32; + + bb0: { + // early: [_1] + // late: [_0] + _0 = callee(move (*_1), const 0_u32) -> [return: bb1, unwind unreachable]; + } + + bb1: { + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.dead_destinations.dataflow.0.mir b/tests/mir-opt/precise_liveness.dead_destinations.dataflow.0.mir new file mode 100644 index 0000000000000..9b6a304bc7184 --- /dev/null +++ b/tests/mir-opt/precise_liveness.dead_destinations.dataflow.0.mir @@ -0,0 +1,31 @@ +// MIR for `dead_destinations` 0 dataflow + +fn dead_destinations() -> u32 { + let mut _0: u32; + let mut _1: u32; + let mut _2: u32; + + bb0: { + // early: [] + // late: [_1] + _1 = const 1_u32; + // early: [] + // late: [_2] + _2 = callee(const 1_u32, const 2_u32) -> [return: bb1, unwind: bb2]; + } + + bb1: { + // early: [] + // late: [_0] + _0 = const 0_u32; + // early: [_0] + // late: [] + return; + } + + bb2 (cleanup): { + // early: [] + // late: [] + resume; + } +} diff --git a/tests/mir-opt/precise_liveness.dereference.dataflow.0.mir b/tests/mir-opt/precise_liveness.dereference.dataflow.0.mir new file mode 100644 index 0000000000000..bb00f3aaf5247 --- /dev/null +++ b/tests/mir-opt/precise_liveness.dereference.dataflow.0.mir @@ -0,0 +1,14 @@ +// MIR for `dereference` 0 dataflow + +fn dereference(_1: *mut u32) -> () { + let mut _0: (); + + bb0: { + // early: [_1] + // late: [] + (*_1) = const 1_u32; + // early: [] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.fields.dataflow.0.mir b/tests/mir-opt/precise_liveness.fields.dataflow.0.mir new file mode 100644 index 0000000000000..2734ebac6301f --- /dev/null +++ b/tests/mir-opt/precise_liveness.fields.dataflow.0.mir @@ -0,0 +1,25 @@ +// MIR for `fields` 0 dataflow + +fn fields() -> u32 { + let mut _0: u32; + let mut _1: (u32, u32); + let mut _2: *const (u32, u32); + + bb0: { + // early: [] + // late: [_1] + (_1.0: u32) = const 1_u32; + // early: [_1] + // late: [_1] + (_1.1: u32) = const 2_u32; + // early: [_1] + // late: [_1, _2] + _2 = &raw const _1; + // early: [_1] + // late: [_0, _1] + _0 = Add(move (_1.0: u32), move (_1.1: u32)); + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.indexed.dataflow.0.mir b/tests/mir-opt/precise_liveness.indexed.dataflow.0.mir new file mode 100644 index 0000000000000..e36103a330aab --- /dev/null +++ b/tests/mir-opt/precise_liveness.indexed.dataflow.0.mir @@ -0,0 +1,15 @@ +// MIR for `indexed` 0 dataflow + +fn indexed(_1: usize) -> () { + let mut _0: (); + let mut _2: [u32; 2]; + + bb0: { + // early: [_1] + // late: [_2] + _2[_1] = const 1_u32; + // early: [] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.rs b/tests/mir-opt/precise_liveness.rs new file mode 100644 index 0000000000000..783da35d037d0 --- /dev/null +++ b/tests/mir-opt/precise_liveness.rs @@ -0,0 +1,357 @@ +//@ needs-unwind + +#![feature(core_intrinsics, custom_mir, rustc_attrs)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// A non-borrowed local dies on its last use. +// EMIT_MIR precise_liveness.unborrowed.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn unborrowed() -> u32 { + // CHECK-LABEL: fn unborrowed( + // CHECK: [[A:_[0-9]+]] = const 1_u32; + // CHECK: [[B:_[0-9]+]] = const 2_u32; + // CHECK: // early: [[[A]], [[B]]] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: _0 = Add(copy [[A]], move [[B]]); + mir! { + let a: u32; + let b: u32; + { + a = 1; + b = 2; + RET = a + Move(b); + Return() + } + } +} + +// A borrowed local only dies on move. +// EMIT_MIR precise_liveness.borrowed.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn borrowed() -> u32 { + // CHECK-LABEL: fn borrowed( + // CHECK: [[A:_[0-9]+]] = const 1_u32; + // CHECK: [[B:_[0-9]+]] = const 2_u32; + // CHECK: = &raw const [[B]]; + // CHECK-NEXT: // early: [[[A]], [[B]]] + // CHECK-NEXT: // late: [_0, [[A]]] + // CHECK-NEXT: _0 = Add(copy [[A]], move [[B]]); + mir! { + let a: u32; + let b: u32; + let a_ptr: *const u32; + let b_ptr: *const u32; + { + a = 1; + b = 2; + a_ptr = &raw const a; + b_ptr = &raw const b; + RET = a + Move(b); + Return() + } + } +} + +// StorageDead kills a lifetime, but it can be re-initialized later. +// EMIT_MIR precise_liveness.storage.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn storage() -> u32 { + // CHECK-LABEL: fn storage( + // CHECK: // early: [_0, [[BORROWED:_[0-9]+]]] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: StorageDead([[BORROWED]]); + // CHECK-NEXT: // early: [_0] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: StorageLive([[BORROWED]]); + // CHECK-NEXT: // early: [_0] + // CHECK-NEXT: // late: [_0, [[BORROWED]]] + // CHECK-NEXT: [[BORROWED]] = const 3_u32; + // CHECK-NEXT: // early: [_0, [[BORROWED]]] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: StorageDead([[BORROWED]]); + // CHECK-NEXT: // early: [_0] + // CHECK-NEXT: // late: [] + // CHECK-NEXT: return; + mir! { + let borrowed: u32; + let pointer: *const u32; + { + StorageLive(borrowed); + borrowed = 2; + pointer = &raw const borrowed; + RET = borrowed; + StorageDead(borrowed); + StorageLive(borrowed); + borrowed = 3; + StorageDead(borrowed); + Return() + } + } +} + +// Partial initialization starts a lifetime, but only a bare local move ends it. +// EMIT_MIR precise_liveness.fields.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn fields() -> u32 { + // CHECK-LABEL: fn fields( + // CHECK: // early: [] + // CHECK-NEXT: // late: [[[AGGREGATE:_[0-9]+]]] + // CHECK-NEXT: ([[AGGREGATE]].0: u32) = const 1_u32; + // CHECK: = &raw const [[AGGREGATE]]; + // CHECK-NEXT: // early: [[[AGGREGATE]]] + // CHECK-NEXT: // late: [_0, [[AGGREGATE]]] + // CHECK-NEXT: _0 = Add(move ([[AGGREGATE]].0: u32), move ([[AGGREGATE]].1: u32)); + mir! { + let aggregate: (u32, u32); + let pointer: *const (u32, u32); + { + aggregate.0 = 1; + aggregate.1 = 2; + pointer = &raw const aggregate; + RET = Move(aggregate.0) + Move(aggregate.1); + Return() + } + } +} + +unsafe extern "C-unwind" { + fn callee(a: u32, b: u32) -> u32; +} + +// Check the behavior around calls: +// - Destination is initialized at the late point. +// - Move arguments have their live range extended to the late point. +// - Copy arguments die at the early point when this is their last use. +// - Destination remains live on unwind edges. +// EMIT_MIR precise_liveness.call.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn call() -> u32 { + // CHECK-LABEL: fn call( + // CHECK: [[A:_[0-9]+]] = const 1_u32; + // CHECK: [[B:_[0-9]+]] = const 2_u32; + // CHECK-NEXT: // early: [[[A]], [[B]]] + // CHECK-NEXT: // late: [[[A]], [[DESTINATION:_[0-9]+]]] + // CHECK-NEXT: [[DESTINATION]] = callee(move [[A]], copy [[B]]) + // CHECK: = &raw const [[DESTINATION]]; + // CHECK-NEXT: // early: [[[DESTINATION]]] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: _0 = move [[DESTINATION]]; + // CHECK: (cleanup): { + // CHECK-NEXT: // early: [[[DESTINATION]]] + // CHECK-NEXT: // late: [[[DESTINATION]]] + // CHECK-NEXT: resume; + mir! { + let a: u32; + let b: u32; + let destination: u32; + let pointer: *const u32; + { + a = 1; + b = 2; + Call(destination = callee(Move(a), b), ReturnTo(done), UnwindCleanup(cleanup)) + } + done = { + pointer = &raw const destination; + RET = Move(destination); + Return() + } + cleanup(cleanup) = { + UnwindResume() + } + } +} + +// A value used on only one branch is not live on the other branch. +// EMIT_MIR precise_liveness.branch.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn branch(condition: bool) -> u32 { + // CHECK-LABEL: fn branch( + // CHECK: [[VALUE:_[0-9]+]] = const 1_u32; + // CHECK-NEXT: // early: [[[CONDITION:_[0-9]+]], [[VALUE]]] + // CHECK-NEXT: // late: [[[VALUE]]] + // CHECK-NEXT: switchInt(copy [[CONDITION]]) -> [1: [[USE:bb[0-9]+]], otherwise: [[SKIP:bb[0-9]+]]]; + // CHECK: [[USE]]: { + // CHECK-NEXT: // early: [[[VALUE]]] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: _0 = move [[VALUE]]; + // CHECK: [[SKIP]]: { + // CHECK-NEXT: // early: [] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: _0 = const 0_u32; + mir! { + let value: u32; + { + value = 1; + match condition { true => use_value, _ => skip_value } + } + use_value = { + RET = Move(value); + Return() + } + skip_value = { + RET = 0; + Return() + } + } +} + +// Dead destinations are live only at the late point of the assignment or call. +// EMIT_MIR precise_liveness.dead_destinations.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn dead_destinations() -> u32 { + // CHECK-LABEL: fn dead_destinations( + // CHECK: // early: [] + // CHECK-NEXT: // late: [[[ASSIGNMENT:_[0-9]+]]] + // CHECK-NEXT: [[ASSIGNMENT]] = const 1_u32; + // CHECK-NEXT: // early: [] + // CHECK-NEXT: // late: [[[DESTINATION:_[0-9]+]]] + // CHECK-NEXT: [[DESTINATION]] = callee(const 1_u32, const 2_u32) -> [return: [[DONE:bb[0-9]+]], unwind: [[CLEANUP:bb[0-9]+]]]; + // CHECK: [[DONE]]: { + // CHECK-NEXT: // early: [] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: _0 = const 0_u32; + // CHECK: [[CLEANUP]] (cleanup): { + // CHECK-NEXT: // early: [] + // CHECK-NEXT: // late: [] + // CHECK-NEXT: resume; + mir! { + let assignment: u32; + let destination: u32; + { + assignment = 1; + Call(destination = callee(1, 2), ReturnTo(done), UnwindCleanup(cleanup)) + } + done = { + RET = 0; + Return() + } + cleanup(cleanup) = { + UnwindResume() + } + } +} + +// An indexed write allocates the array. The index is a use, not a destination. +// EMIT_MIR precise_liveness.indexed.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn indexed(index: usize) { + // CHECK-LABEL: fn indexed( + // CHECK: // early: [[[INDEX:_[0-9]+]]] + // CHECK-NEXT: // late: [[[ARRAY:_[0-9]+]]] + // CHECK-NEXT: [[ARRAY]][[[INDEX]]] = const 1_u32; + mir! { + let array: [u32; 2]; + { + array[index] = 1; + Return() + } + } +} + +// A write through a pointer is a use of the pointer, not a def. +// EMIT_MIR precise_liveness.dereference.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn dereference(pointer: *mut u32) { + // CHECK-LABEL: fn dereference( + // CHECK: // early: [[[POINTER:_[0-9]+]]] + // CHECK-NEXT: // late: [] + // CHECK-NEXT: (*[[POINTER]]) = const 1_u32; + mir! { + { + *pointer = 1; + Return() + } + } +} + +// A field move keeps the base allocation live through the call's late point. +// EMIT_MIR precise_liveness.call_field.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn call_field() -> u32 { + // CHECK-LABEL: fn call_field( + // CHECK: ([[AGGREGATE:_[0-9]+]].0: u32) = const 1_u32; + // CHECK-NEXT: // early: [[[AGGREGATE]]] + // CHECK-NEXT: // late: [_0, [[AGGREGATE]]] + // CHECK-NEXT: _0 = callee(move ([[AGGREGATE]].0: u32), const 0_u32) + // CHECK: // early: [_0] + // CHECK-NEXT: // late: [] + // CHECK-NEXT: return; + mir! { + let aggregate: (u32,); + { + aggregate.0 = 1; + Call(RET = callee(Move(aggregate.0), 0), ReturnTo(done), UnwindUnreachable()) + } + done = { + Return() + } + } +} + +// A borrowed field's base remains live in both successors after a field move. +// EMIT_MIR precise_liveness.call_borrowed_field.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn call_borrowed_field() -> u32 { + // CHECK-LABEL: fn call_borrowed_field( + // CHECK: = &raw const [[AGGREGATE:_[0-9]+]]; + // CHECK-NEXT: // early: [[[AGGREGATE]]] + // CHECK-NEXT: // late: [[[AGGREGATE]], [[DESTINATION:_[0-9]+]]] + // CHECK-NEXT: [[DESTINATION]] = callee(move ([[AGGREGATE]].0: u32), const 0_u32) -> [return: [[DONE:bb[0-9]+]], unwind: [[CLEANUP:bb[0-9]+]]]; + // CHECK: [[DONE]]: { + // CHECK-NEXT: // early: [[[AGGREGATE]], [[DESTINATION]]] + // CHECK-NEXT: // late: [_0, [[AGGREGATE]]] + // CHECK-NEXT: _0 = move [[DESTINATION]]; + // CHECK: [[CLEANUP]] (cleanup): { + // CHECK-NEXT: // early: [[[AGGREGATE]]] + // CHECK-NEXT: // late: [[[AGGREGATE]]] + // CHECK-NEXT: resume; + mir! { + let aggregate: (u32,); + let destination: u32; + let borrow: *const (u32,); + { + aggregate.0 = 1; + borrow = &raw const aggregate; + Call(destination = callee(Move(aggregate.0), 0), ReturnTo(done), UnwindCleanup(cleanup)) + } + done = { + RET = Move(destination); + Return() + } + cleanup(cleanup) = { + UnwindResume() + } + } +} + +// An indirect move does not extend the pointer local's lifetime to the late point. +// EMIT_MIR precise_liveness.call_indirect.dataflow.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[rustc_mir(rustc_pretty_precise_liveness)] +pub fn call_indirect(pointer: *const u32) -> u32 { + // CHECK-LABEL: fn call_indirect( + // CHECK: // early: [[[POINTER:_[0-9]+]]] + // CHECK-NEXT: // late: [_0] + // CHECK-NEXT: _0 = callee(move (*[[POINTER]]), const 0_u32) + mir! { + { + Call(RET = callee(Move(*pointer), 0), ReturnTo(done), UnwindUnreachable()) + } + done = { + Return() + } + } +} diff --git a/tests/mir-opt/precise_liveness.storage.dataflow.0.mir b/tests/mir-opt/precise_liveness.storage.dataflow.0.mir new file mode 100644 index 0000000000000..56ae734839ee9 --- /dev/null +++ b/tests/mir-opt/precise_liveness.storage.dataflow.0.mir @@ -0,0 +1,37 @@ +// MIR for `storage` 0 dataflow + +fn storage() -> u32 { + let mut _0: u32; + let mut _1: u32; + let mut _2: *const u32; + + bb0: { + // early: [] + // late: [] + StorageLive(_1); + // early: [] + // late: [_1] + _1 = const 2_u32; + // early: [_1] + // late: [_1, _2] + _2 = &raw const _1; + // early: [_1] + // late: [_0, _1] + _0 = copy _1; + // early: [_0, _1] + // late: [_0] + StorageDead(_1); + // early: [_0] + // late: [_0] + StorageLive(_1); + // early: [_0] + // late: [_0, _1] + _1 = const 3_u32; + // early: [_0, _1] + // late: [_0] + StorageDead(_1); + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/mir-opt/precise_liveness.unborrowed.dataflow.0.mir b/tests/mir-opt/precise_liveness.unborrowed.dataflow.0.mir new file mode 100644 index 0000000000000..4a27e7b0f9d5a --- /dev/null +++ b/tests/mir-opt/precise_liveness.unborrowed.dataflow.0.mir @@ -0,0 +1,22 @@ +// MIR for `unborrowed` 0 dataflow + +fn unborrowed() -> u32 { + let mut _0: u32; + let mut _1: u32; + let mut _2: u32; + + bb0: { + // early: [] + // late: [_1] + _1 = const 1_u32; + // early: [_1] + // late: [_1, _2] + _2 = const 2_u32; + // early: [_1, _2] + // late: [_0] + _0 = Add(copy _1, move _2); + // early: [_0] + // late: [] + return; + } +} diff --git a/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout b/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout index 22f4fe70c6175..248f7734652fa 100644 --- a/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout +++ b/tests/rustdoc-ui/remap-path-prefix-doctest.with-diag-scope.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED +test remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED failures: ----- $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- +---- remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- error: this file contains an unclosed delimiter - --> $DIR/remap-path-prefix-doctest.rs:20:12 + --> remapped_path/remap-path-prefix-doctest.rs:20:12 | LL | fn invalid( | -^ @@ -18,7 +18,7 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) + remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout b/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout index 248f7734652fa..22f4fe70c6175 100644 --- a/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout +++ b/tests/rustdoc-ui/remap-path-prefix-doctest.with-doc-scope.stdout @@ -1,12 +1,12 @@ running 1 test -test remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED +test $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) ... FAILED failures: ----- remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- +---- $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) stdout ---- error: this file contains an unclosed delimiter - --> remapped_path/remap-path-prefix-doctest.rs:20:12 + --> $DIR/remap-path-prefix-doctest.rs:20:12 | LL | fn invalid( | -^ @@ -18,7 +18,7 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - remapped_path/remap-path-prefix-doctest.rs - SomeStruct (line 19) + $DIR/remap-path-prefix-doctest.rs - SomeStruct (line 19) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/remap-path-prefix-file.all_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.all_scope.stdout new file mode 100644 index 0000000000000..910e5a362cabb --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.all_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test /REMAPPED/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.debuginfo_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.debuginfo_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.debuginfo_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.diagnostics_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.diagnostics_scope.stdout new file mode 100644 index 0000000000000..910e5a362cabb --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.diagnostics_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test /REMAPPED/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.documentation_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.documentation_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.documentation_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.macro_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.macro_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.macro_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.no_scopes.stdout b/tests/rustdoc-ui/remap-path-prefix-file.no_scopes.stdout new file mode 100644 index 0000000000000..910e5a362cabb --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.no_scopes.stdout @@ -0,0 +1,6 @@ + +running 1 test +test /REMAPPED/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.object_scope.stdout b/tests/rustdoc-ui/remap-path-prefix-file.object_scope.stdout new file mode 100644 index 0000000000000..72c61da715f74 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.object_scope.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/remap-path-prefix-file.rs - f (line 24) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-file.rs b/tests/rustdoc-ui/remap-path-prefix-file.rs new file mode 100644 index 0000000000000..b9fbe77bd91e6 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-file.rs @@ -0,0 +1,38 @@ +// Checks that macros uses inside doctests are correctly gated under the +// `macro` and al --remap-path-scope scopes. + +//@ only-linux +//@ compile-flags: --test --test-args=--test-threads=1 +//@ compile-flags: --remap-path-prefix={{src-base}}=/REMAPPED +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ rustc-env: RUST_BACKTRACE=0 +//@ check-pass + +//@ revisions: macro_scope diagnostics_scope documentation_scope debuginfo_scope +//@ revisions: object_scope all_scope no_scopes + +//@[macro_scope] compile-flags: --remap-path-scope=macro -Z unstable-options +//@[diagnostics_scope] compile-flags: --remap-path-scope=diagnostics -Z unstable-options +//@[documentation_scope] compile-flags: --remap-path-scope=documentation -Z unstable-options +//@[debuginfo_scope] compile-flags: --remap-path-scope=debuginfo -Z unstable-options +//@[object_scope] compile-flags: --remap-path-scope=object -Z unstable-options +//@[all_scope] compile-flags: --remap-path-scope=all -Z unstable-options +// `no_scopes` passes no --remap-path-scope, so defaults to `all` + +/// Only `macro`, `object` and `all` should remap what `file!()` expands to. +/// +/// ``` +/// #[cfg(any(macro_scope, object_scope, all_scope, no_scopes))] +/// const EXPECT_REMAPPED: bool = true; +/// #[cfg(not(any(macro_scope, object_scope, all_scope, no_scopes)))] +/// const EXPECT_REMAPPED: bool = false; +/// +/// let file = file!(); +/// assert_eq!( +/// file.starts_with("/REMAPPED"), +/// EXPECT_REMAPPED, +/// "unexpected file!() = {file}", +/// ); +/// assert!(!file.starts_with("/tmp"), "/tmp leaked: {}", file); +/// ``` +pub fn f() {} diff --git a/tests/ui/impl-trait/precise-capturing/parenthesized.rs b/tests/ui/impl-trait/precise-capturing/parenthesized.rs index e3f80fc1d9f06..6ecdf5d7ddbb3 100644 --- a/tests/ui/impl-trait/precise-capturing/parenthesized.rs +++ b/tests/ui/impl-trait/precise-capturing/parenthesized.rs @@ -5,4 +5,14 @@ fn f() -> impl Sized + (use<>) {} //~^ ERROR precise capturing lists may not be parenthesized //~| HELP remove the parentheses +#[cfg(false)] +type O = Trait + (use<>); +//~^ ERROR precise capturing lists may not be parenthesized +//~| HELP remove the parentheses + +// We once used to accidentally accept this. +#[cfg(false)] +type O = (use<>) + Trait; +//~^ ERROR expected a path on the left-hand side of `+` + fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/parenthesized.stderr b/tests/ui/impl-trait/precise-capturing/parenthesized.stderr index c97fa9972ef01..b01f02bbafa89 100644 --- a/tests/ui/impl-trait/precise-capturing/parenthesized.stderr +++ b/tests/ui/impl-trait/precise-capturing/parenthesized.stderr @@ -10,5 +10,24 @@ LL - fn f() -> impl Sized + (use<>) {} LL + fn f() -> impl Sized + use<> {} | -error: aborting due to 1 previous error +error: precise capturing lists may not be parenthesized + --> $DIR/parenthesized.rs:9:18 + | +LL | type O = Trait + (use<>); + | ^^^^^^^ + | +help: remove the parentheses + | +LL - type O = Trait + (use<>); +LL + type O = Trait + use<>; + | + +error[E0178]: expected a path on the left-hand side of `+` + --> $DIR/parenthesized.rs:15:10 + | +LL | type O = (use<>) + Trait; + | ^^^^^^^ expected a path + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0178`.