diff --git a/Cargo.lock b/Cargo.lock index 2f4d5f7f786e..9c6d6c22de4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4101,6 +4101,7 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ + "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_ast_lowering", @@ -4602,7 +4603,6 @@ version = "0.0.0" dependencies = [ "getopts", "libc", - "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_data_structures", diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4644c210137f..6d6870364231 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -2,11 +2,12 @@ use std::ops::Index; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_hir::Mutability; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; -use rustc_middle::span_bug; use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; @@ -300,6 +301,50 @@ fn visit_assign( idx }; + self.local_map.entry(borrowed_place.local).or_default().insert(idx); + } else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue { + let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; + let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else { + unreachable!() + }; + let &ty::Adt(target_adt, assigned_args) = target.kind() else { unreachable!() }; + let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind()) + else { + bug!( + "hir-typeck passed but {} does not have a lifetime argument", + if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" } + ); + }; + let region = region.as_var(); + let kind = if mutability == Mutability::Mut { + // Reborrow + if target_adt.did() != reborrowed_adt.did() { + bug!( + "hir-typeck passed but Reborrow involves mismatching types at {location:?}" + ) + } + + mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default } + } else { + // CoerceShared + if target_adt.did() == reborrowed_adt.did() { + bug!( + "hir-typeck passed but CoerceShared involves matching types at {location:?}" + ) + } + mir::BorrowKind::Shared + }; + let borrow = BorrowData { + kind, + region, + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place, + assigned_place: *assigned_place, + }; + let (idx, _) = self.location_map.insert_full(location, borrow); + let idx = BorrowIndex::from(idx); + self.local_map.entry(borrowed_place.local).or_default().insert(idx); } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 8ddfa4b61edd..ef167644f0a9 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -549,7 +549,7 @@ fn apply_primary_statement_effect( ) { match &stmt.kind { mir::StatementKind::Assign(box (lhs, rhs)) => { - if let mir::Rvalue::Ref(_, _, place) = rhs { + if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, _, place) = rhs { if place.ignore_borrow( self.tcx, self.body, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 168157cf2920..d19d73550140 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1270,6 +1270,7 @@ fn check_access_for_conflict( let mut error_reported = false; let borrows_in_scope = self.borrows_in_scope(location, state); + debug!(?borrows_in_scope, ?location); each_borrow_involving_path( self, @@ -1512,6 +1513,36 @@ fn consume_rvalue( ); } + &Rvalue::Reborrow(_target, mutability, place) => { + let access_kind = ( + Deep, + if mutability == Mutability::Mut { + Write(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::Default, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, + ); + + self.access_place( + location, + (place, span), + access_kind, + LocalMutationIsAllowed::Yes, + state, + ); + + let action = InitializationRequiringAction::Borrow; + + self.check_if_path_or_subpath_is_moved( + location, + action, + (place.as_ref(), span), + state, + ); + } + &Rvalue::RawPtr(kind, place) => { let access_kind = match kind { RawPtrKind::Mut => ( diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 136719a323ce..da3fc0763a43 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -274,6 +274,21 @@ fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } + &Rvalue::Reborrow(_target, mutability, place) => { + let access_kind = ( + Deep, + if mutability == Mutability::Mut { + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::TwoPhaseBorrow, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, + ); + + self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); + } + &Rvalue::RawPtr(kind, place) => { let access_kind = match kind { RawPtrKind::Mut => ( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8998ced10bf9..48389d935499 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1580,6 +1580,15 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.add_reborrow_constraint(location, *region, borrowed_place); } + Rvalue::Reborrow(target, mutability, borrowed_place) => { + self.add_generic_reborrow_constraint( + *mutability, + location, + borrowed_place, + *target, + ); + } + Rvalue::BinaryOp( BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, box (left, right), @@ -2218,6 +2227,7 @@ fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option, + dest_ty: Ty<'tcx>, + ) { + let Self { borrow_set, location_table, polonius_facts, constraints, infcx, body, .. } = + self; + + debug!( + "add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})", + mutability, location, borrowed_place, dest_ty + ); + + let tcx = infcx.tcx; + let def = body.source.def_id().expect_local(); + let upvars = tcx.closure_captures(def); + let field = + path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body); + let category = if let Some(field) = field { + ConstraintCategory::ClosureUpvar(field) + } else { + ConstraintCategory::Boring + }; + + let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; + + let ty::Adt(dest_adt, dest_args) = dest_ty.kind() else { bug!() }; + let [dest_arg, ..] = ***dest_args else { bug!() }; + let ty::GenericArgKind::Lifetime(dest_region) = dest_arg.kind() else { bug!() }; + constraints.liveness_constraints.add_location(dest_region.as_var(), location); + + // In Polonius mode, we also push a `loan_issued_at` fact + // linking the loan to the region. + if let Some(polonius_facts) = polonius_facts { + let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation"); + if let Some(borrow_index) = borrow_set.get_index_of(&location) { + let region_vid = dest_region.as_var(); + polonius_facts.loan_issued_at.push(( + region_vid.into(), + borrow_index, + location_table.mid_index(location), + )); + } + } + + if mutability.is_not() { + // FIXME(reborrow): for CoerceShared we need to relate the types manually, field by + // field. We cannot just attempt to relate `T` and `::Target` by + // calling relate_types as they are (generally) two unrelated user-defined ADTs, such as + // `CustomMut<'a>` and `CustomRef<'a>`, or `CustomMut<'a, T>` and `CustomRef<'a, T>`. + // Field-by-field relate_types is expected to work based on the wf-checks that the + // CoerceShared trait performs. + let ty::Adt(borrowed_adt, borrowed_args) = borrowed_ty.kind() else { unreachable!() }; + let borrowed_fields = borrowed_adt.all_fields().collect::>(); + for dest_field in dest_adt.all_fields() { + let Some(borrowed_field) = + borrowed_fields.iter().find(|f| f.name == dest_field.name) + else { + continue; + }; + let dest_ty = dest_field.ty(tcx, dest_args); + let borrowed_ty = borrowed_field.ty(tcx, borrowed_args); + if let ( + ty::Ref(borrow_region, _, Mutability::Mut), + ty::Ref(ref_region, _, Mutability::Not), + ) = (borrowed_ty.kind(), dest_ty.kind()) + { + self.relate_types( + borrowed_ty.peel_refs(), + ty::Variance::Covariant, + dest_ty.peel_refs(), + location.to_locations(), + category, + ) + .unwrap(); + self.constraints.outlives_constraints.push(OutlivesConstraint { + sup: ref_region.as_var(), + sub: borrow_region.as_var(), + locations: location.to_locations(), + span: location.to_locations().span(self.body), + category, + variance_info: ty::VarianceDiagInfo::default(), + from_closure: false, + }); + } else { + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } + } + } else { + // Exclusive reborrow + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } + } + fn prove_aggregate_predicates( &mut self, aggregate_kind: &AggregateKind<'tcx>, diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 7ba4475e3145..7194d8144ca6 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -16,6 +16,7 @@ index 1e336bf..35e6f54 100644 +++ b/coretests/tests/lib.rs @@ -2,4 +2,3 @@ // tidy-alphabetical-start + #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] @@ -36,4 +37,3 @@ index b735957..ea728b6 100644 #[cfg(target_has_atomic = "ptr")] -- 2.26.2.7.g19db9cfb68 - diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 0b7e308b9489..4a663c48c0af 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -200,5 +200,5 @@ index 073116933bd..c3e4578204d 100644 EOF echo "[TEST] rustc test suite" -./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,run-make-cargo,ui,incremental} +./x.py test --stage 0 --no-capture --verbose-run-make-subprocess-output=false tests/{codegen-units,run-make,run-make-cargo,ui,incremental} popd diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3d15ad819310..1d90c8e0dadc 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -629,6 +629,11 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let ref_ = place.place_ref(fx, lval.layout()); lval.write_cvalue(fx, ref_); } + Rvalue::Reborrow(_, _, place) => { + let cplace = codegen_place(fx, place); + let val = cplace.to_cvalue(fx); + lval.write_cvalue(fx, val) + } Rvalue::ThreadLocalRef(def_id) => { let val = crate::constant::codegen_tls_ref(fx, def_id, lval.layout()); lval.write_cvalue(fx, val); diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index bea254b7b3a6..a9acd0ae34ca 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -150,7 +150,6 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { fn emit_cgu( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, name: String, module: UnwindModule, @@ -166,7 +165,6 @@ fn emit_cgu( let module_regular = emit_module( output_filenames, - invocation_temp, prof, product.object, ModuleKind::Regular, @@ -192,7 +190,6 @@ fn emit_cgu( fn emit_module( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, @@ -211,7 +208,7 @@ fn emit_module( object.set_section_data(comment_section, producer, 1); } - let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp); + let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); let file = match File::create(&tmp_file) { Ok(file) => file, Err(err) => return Err(format!("error creating object file: {}", err)), @@ -251,11 +248,8 @@ fn reuse_workproduct_for_cgu( cgu: &CodegenUnit<'_>, ) -> Result { let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - cgu.name().as_str(), - tcx.sess.invocation_temp.as_deref(), - ); + let obj_out_regular = + tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( tcx.sess, work_product.saved_files.get("o").expect("no saved object file in work product"), @@ -394,7 +388,6 @@ fn module_codegen( let producer = crate::debuginfo::producer(tcx.sess); let profiler = tcx.prof.clone(); - let invocation_temp = tcx.sess.invocation_temp.clone(); let output_filenames = tcx.output_filenames(()).clone(); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); @@ -421,19 +414,13 @@ fn module_codegen( let global_asm_object_file = profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm( - &global_asm_config, - &cgu_name, - global_asm, - invocation_temp.as_deref(), - ) + crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm) })?; let codegen_result = profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, - invocation_temp.as_deref(), &profiler, cgu_name, module, @@ -456,7 +443,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { match emit_module( tcx.output_filenames(()), - tcx.sess.invocation_temp.as_deref(), &tcx.sess.prof, product.object, ModuleKind::Allocator, diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 3903e6ea3b1d..33b88d70d6f8 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -33,14 +33,15 @@ fn create_jit_module( (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, jit_args: Vec) -> ! { if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { tcx.dcx().fatal("can't jit non-executable crate"); } let output_filenames = tcx.output_filenames(()); + let crate_info = CrateInfo::new(tcx, target_cpu); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - let (mut jit_module, mut debug_context) = create_jit_module(tcx, crate_info); + let (mut jit_module, mut debug_context) = create_jit_module(tcx, &crate_info); let mut cached_context = Context::new(); let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 5765601763e4..0c5f4136a32d 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -185,7 +185,6 @@ pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, cgu_name: &str, global_asm: String, - invocation_temp: Option<&str>, ) -> Result, String> { if global_asm.is_empty() { return Ok(None); @@ -200,7 +199,7 @@ pub(crate) fn compile_global_asm( global_asm.push('\n'); let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp), + config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name), ".asm", ); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index cbbb0ccbbc21..361f143d9991 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -209,12 +209,12 @@ fn target_cpu(&self, sess: &Session) -> String { .to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.get().unwrap(); if config.jit_mode { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone()); + driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone()); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -228,6 +228,7 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 64674423de2c..8fd38a2efd60 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -29,16 +29,8 @@ pub(crate) fn codegen( let lto_mode = module.module_llvm.lto_mode; let lto_supported = module.module_llvm.lto_supported; - let bc_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Bitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); - let obj_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name); + let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name); if config.bitcode_needed() { let _timer = @@ -82,22 +74,15 @@ pub(crate) fn codegen( } if config.emit_ir { - let out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::LlvmAssembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); std::fs::write(out, "").expect("write file"); } if config.emit_asm { let _timer = prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Assembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name); context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str")); } @@ -215,7 +200,6 @@ pub(crate) fn codegen( config.emit_asm, config.emit_ir, &cgcx.output_filenames, - cgcx.invocation_temp.as_deref(), ) } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 4be25b3fb093..6ca2ef88ef29 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -291,8 +291,8 @@ fn target_cpu(&self, sess: &Session) -> String { target_cpu(sess).to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, crate_info: &CrateInfo) -> Box { - Box::new(codegen_crate(self.clone(), tcx, crate_info)) + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { + Box::new(codegen_crate(self.clone(), tcx)) } fn join_codegen( @@ -300,11 +300,12 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") - .join(sess) + .join(sess, crate_info) } fn target_config(&self, sess: &Session) -> TargetConfig { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 515b571a9f4b..417f321a6b4a 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -117,17 +117,13 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, mod_name, - tcx.sess.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - mod_name, - tcx.sess.invocation_temp.as_deref(), - )); + let output_obj_file = + Some(tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, mod_name)); let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; target_machine_factory( @@ -322,11 +318,7 @@ pub(crate) fn save_temp_bitcode( return; } let ext = format!("{name}.bc"); - let path = cgcx.output_filenames.temp_path_ext_for_cgu( - &ext, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_ext_for_cgu(&ext, &module.name); write_bitcode_to_file(&module.module_llvm, &path) } @@ -949,11 +941,8 @@ pub(crate) fn optimize( if let Some(thin_lto_buffer) = thin_lto_buffer { let thin_lto_buffer = thin_lto_buffer.unwrap(); module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); - let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::ThinLinkBitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_summary_out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name); if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { @@ -1008,16 +997,8 @@ pub(crate) fn codegen( // copy it to the .o file, and delete the bitcode if it wasn't // otherwise requested. - let bc_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Bitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); - let obj_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name); + let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name); if config.bitcode_needed() { if config.emit_bc || config.emit_obj == EmitObj::Bitcode { @@ -1055,11 +1036,8 @@ pub(crate) fn codegen( if config.emit_ir { let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name); - let out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::LlvmAssembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); let out_c = path_to_c_string(&out); extern "C" fn demangle_callback( @@ -1103,11 +1081,7 @@ extern "C" fn demangle_callback( if config.emit_asm { let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Assembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name); // We can't use the same module for asm and object code output, // because that triggers various errors like invalid IR or broken @@ -1136,9 +1110,7 @@ extern "C" fn demangle_callback( let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); - let dwo_out = cgcx - .output_filenames - .temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref()); + let dwo_out = cgcx.output_filenames.temp_path_dwo_for_cgu(&module.name); let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) { // Don't change how DWARF is emitted when disabled. (SplitDebuginfo::Off, _) => None, @@ -1203,7 +1175,6 @@ extern "C" fn demangle_callback( config.emit_asm, config.emit_ir, &cgcx.output_filenames, - cgcx.invocation_temp.as_deref(), ) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 9a9e8287e787..19dbdc6946b7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -903,7 +903,6 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, codegen_unit_name, - tcx.sess.invocation_temp.as_deref(), ) { // We get a path relative to the working directory from split_dwarf_path Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f)) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index dd9ebf298b22..84c1e8e6f3d4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2086,7 +2086,7 @@ fn vector_mask_to_bitmask<'a, 'll, 'tcx>( } if name == sym::simd_splat { - let (_out_len, out_ty) = require_simd!(ret_ty, SimdReturn); + let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); require!( args[0].layout.ty == out_ty, @@ -2105,7 +2105,8 @@ fn vector_mask_to_bitmask<'a, 'll, 'tcx>( // `shufflevector v0, poison, zeroinitializer` // The masks is all zeros, so this splats lane 0 (which has our element in it). - let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(llret_ty)); + let mask_ty = bx.type_vector(bx.type_i32(), out_len); + let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(mask_ty)); return Ok(splat); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 575e37d0b171..b273047b98fc 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -333,8 +333,8 @@ fn target_cpu(&self, sess: &Session) -> String { crate::llvm_util::target_cpu(sess).to_string() } - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box { - Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info)) + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx)) } fn join_codegen( @@ -342,11 +342,12 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { let (compiled_modules, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") - .join(sess); + .join(sess, crate_info); if sess.opts.unstable_opts.llvm_time_trace { sess.time("llvm_dump_timing_file", || { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d8e2b54ad50a..9368bc6e1ba7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -112,12 +112,7 @@ pub fn link_binary( let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); let crate_name = format!("{}", crate_info.local_crate_name); - let out_filename = output.file_for_writing( - outputs, - OutputType::Exe, - &crate_name, - sess.invocation_temp.as_deref(), - ); + let out_filename = output.file_for_writing(outputs, OutputType::Exe, &crate_name); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index ddfcd8a85f6b..ed36fae3cdc4 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -85,7 +85,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool { } } -pub(super) fn exported_symbols_for_lto( +pub(crate) fn exported_symbols_for_lto( tcx: TyCtxt<'_>, each_linked_rlib_for_lto: &[CrateNum], ) -> Vec { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c48e8a58b696..7b22ac231df1 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -34,7 +34,7 @@ use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; -use crate::back::link::{self, ensure_removed}; +use crate::back::link::ensure_removed; use crate::back::lto::{self, SerializedModule, check_lto_allowed}; use crate::errors::ErrorCreatingRemarkDir; use crate::traits::*; @@ -136,7 +136,8 @@ macro_rules! if_regular { let emit_obj = if !should_emit_obj { EmitObj::None } else if sess.target.obj_is_bitcode - || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) + || (sess.opts.cg.linker_plugin_lto.enabled() + && (!no_builtins || tcx.sess.is_sanitizer_cfi_enabled())) { // This case is selected if the target uses objects as bitcode, or // if linker plugin LTO is enabled. In the linker plugin LTO case @@ -144,14 +145,23 @@ macro_rules! if_regular { // and convert it to object code. This may be done by either the // native linker or rustc itself. // - // Note, however, that the linker-plugin-lto requested here is - // explicitly ignored for `#![no_builtins]` crates. These crates are - // specifically ignored by rustc's LTO passes and wouldn't work if - // loaded into the linker. These crates define symbols that LLVM - // lowers intrinsics to, and these symbol dependencies aren't known - // until after codegen. As a result any crate marked - // `#![no_builtins]` is assumed to not participate in LTO and - // instead goes on to generate object code. + // By default this branch is skipped for `#![no_builtins]` crates so + // they emit native object files (machine code), not LLVM bitcode + // objects for the linker (see rust-lang/rust#146133). + // + // However, when LLVM CFI is enabled (`-Zsanitizer=cfi`), this + // breaks LLVM's expected pipeline: LLVM emits `llvm.type.test` + // intrinsics and related metadata that must be lowered by LLVM's + // `LowerTypeTests` pass before instruction selection during + // link-time LTO. Otherwise, `llvm.type.test` intrinsics and related + // metadata are not lowered by LLVM's `LowerTypeTests` pass before + // reaching the target backend, and LLVM may abort during codegen + // (for example in SelectionDAG type legalization) (see + // rust-lang/rust#142284). + // + // Therefore, with `-Clinker-plugin-lto` and `-Zsanitizer=cfi`, a + // `#![no_builtins]` crate must still use rustc's `EmitObj::Bitcode` + // path (and emit LLVM bitcode in the `.o` for linker-based LTO). EmitObj::Bitcode } else if need_bitcode_in_object(tcx) || sess.target.requires_lto { EmitObj::ObjectCode(BitcodeSection::Full) @@ -285,17 +295,13 @@ pub fn new(cgcx: &CodegenContext, module_name: &str) -> TargetMachineFactoryConf cgcx.split_debuginfo, cgcx.split_dwarf_kind, module_name, - cgcx.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - module_name, - cgcx.invocation_temp.as_deref(), - )); + let output_obj_file = + Some(cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, module_name)); TargetMachineFactoryConfig { split_dwarf_file, output_obj_file } } } @@ -322,7 +328,6 @@ pub struct CodegenContext { pub time_trace: bool, pub crate_types: Vec, pub output_filenames: Arc, - pub invocation_temp: Option, pub module_config: Arc, pub opt_level: OptLevel, pub backend_features: Vec, @@ -389,18 +394,8 @@ fn generate_thin_lto_work( enum MaybeLtoModules { NoLto(CompiledModules), - FatLto { - cgcx: CodegenContext, - exported_symbols_for_lto: Arc>, - each_linked_rlib_file_for_lto: Vec, - needs_fat_lto: Vec>, - }, - ThinLto { - cgcx: CodegenContext, - exported_symbols_for_lto: Arc>, - each_linked_rlib_file_for_lto: Vec, - needs_thin_lto: Vec>, - }, + FatLto { cgcx: CodegenContext, needs_fat_lto: Vec> }, + ThinLto { cgcx: CodegenContext, needs_thin_lto: Vec> }, } fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool { @@ -424,7 +419,6 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, - crate_info: &CrateInfo, allocator_module: Option>, ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); @@ -440,7 +434,6 @@ pub(crate) fn start_async_codegen( let coordinator_thread = start_executing_work( backend.clone(), tcx, - crate_info, shared_emitter, codegen_worker_send, coordinator_receive, @@ -529,11 +522,7 @@ pub fn produce_final_output_artifacts( if let [module] = &compiled_modules.modules[..] { // 1) Only one codegen unit. In this case it's no difficulty // to copy `foo.0.x` to `foo.x`. - let path = crate_output.temp_path_for_cgu( - output_type, - &module.name, - sess.invocation_temp.as_deref(), - ); + let path = crate_output.temp_path_for_cgu(output_type, &module.name); let output = crate_output.path(output_type); if !output_type.is_text_output() && output.is_tty() { sess.dcx() @@ -912,12 +901,7 @@ fn execute_copy_from_cache_work_item( module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { let dwarf_obj_out = cgcx .output_filenames - .split_dwarf_path( - cgcx.split_debuginfo, - cgcx.split_dwarf_kind, - &module.name, - cgcx.invocation_temp.as_deref(), - ) + .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, &module.name) .expect( "saved dwarf object in work product but `split_dwarf_path` returned `None`", ); @@ -927,11 +911,7 @@ fn execute_copy_from_cache_work_item( let mut load_from_incr_cache = |perform, output_type: OutputType| { if perform { let saved_file = module.source.saved_files.get(output_type.extension())?; - let output_path = cgcx.output_filenames.temp_path_for_cgu( - output_type, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name); load_from_incr_comp_dir(output_path, &saved_file) } else { None @@ -992,8 +972,8 @@ fn do_thin_lto( prof: &SelfProfilerRef, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, - exported_symbols_for_lto: Arc>, - each_linked_rlib_for_lto: Vec, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec>, ) -> Vec { let _timer = prof.verbose_generic_activity("LLVM_thinlto"); @@ -1231,7 +1211,6 @@ enum MainThreadState { fn start_executing_work( backend: B, tcx: TyCtxt<'_>, - crate_info: &CrateInfo, shared_emitter: SharedEmitter, codegen_worker_send: Sender, coordinator_receive: Receiver>, @@ -1243,22 +1222,9 @@ fn start_executing_work( let sess = tcx.sess; let prof = sess.prof.clone(); - let mut each_linked_rlib_for_lto = Vec::new(); - let mut each_linked_rlib_file_for_lto = Vec::new(); - if sess.lto() != Lto::No && sess.lto() != Lto::ThinLocal { - drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return; - } - - each_linked_rlib_for_lto.push(cnum); - each_linked_rlib_file_for_lto.push(path.to_path_buf()); - })); - } - - // Compute the set of symbols we need to retain when doing LTO (if we need to) + // Compute the set of symbols we need to retain when doing thin local LTO (if we need to) let exported_symbols_for_lto = - Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto)); + if sess.lto() == Lto::ThinLocal { lto::exported_symbols_for_lto(tcx, &[]) } else { vec![] }; // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. @@ -1313,7 +1279,6 @@ fn start_executing_work( split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, pointer_size: tcx.data_layout.pointer_size(), - invocation_temp: sess.invocation_temp.clone(), }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1757,12 +1722,7 @@ enum CodegenState { needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, bitcode_path }) } - return Ok(MaybeLtoModules::FatLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_fat_lto, - }); + return Ok(MaybeLtoModules::FatLto { cgcx, needs_fat_lto }); } else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() { assert!(compiled_modules.is_empty()); assert!(needs_fat_lto.is_empty()); @@ -1777,8 +1737,8 @@ enum CodegenState { &prof, shared_emitter.clone(), tm_factory, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, + &exported_symbols_for_lto, + &[], needs_thin_lto, )); } else { @@ -1790,12 +1750,7 @@ enum CodegenState { }); } - return Ok(MaybeLtoModules::ThinLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_thin_lto, - }); + return Ok(MaybeLtoModules::ThinLto { cgcx, needs_thin_lto }); } } @@ -2139,7 +2094,11 @@ pub struct OngoingCodegen { } impl OngoingCodegen { - pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap) { + pub fn join( + self, + sess: &Session, + crate_info: &CrateInfo, + ) -> (CompiledModules, FxIndexMap) { self.shared_emitter_main.check(sess, true); let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() { @@ -2163,12 +2122,7 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap { + MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => { let tm_factory = self.backend.target_machine_factory( sess, cgcx.opt_level, @@ -2181,19 +2135,14 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap { + MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => { let tm_factory = self.backend.target_machine_factory( sess, cgcx.opt_level, @@ -2206,8 +2155,8 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap, kind: AllocatorKind) -> Vec( - backend: B, - tcx: TyCtxt<'_>, - crate_info: &CrateInfo, -) -> OngoingCodegen { +pub fn codegen_crate(backend: B, tcx: TyCtxt<'_>) -> OngoingCodegen { if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { // The target has no default cpu, but none is set explicitly tcx.dcx().emit_fatal(errors::CpuRequired); @@ -734,7 +730,7 @@ pub fn codegen_crate( None }; - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, crate_info, allocator_module); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -780,14 +776,14 @@ pub fn codegen_crate( // This likely is a temporary measure. Once we don't have to support the // non-parallel compiler anymore, we can compile CGUs end-to-end in // parallel and get rid of the complicated scheduling logic. - let mut pre_compiled_cgus = if tcx.sess.threads() > 1 { + let mut pre_compiled_cgus = if let Some(threads) = tcx.sess.threads() { tcx.sess.time("compile_first_CGU_batch", || { // Try to find one CGU to compile per thread. let cgus: Vec<_> = cgu_reuse .iter() .enumerate() .filter(|&(_, reuse)| reuse == &CguReuse::No) - .take(tcx.sess.threads()) + .take(threads) .collect(); // Compile the found CGUs in parallel. @@ -959,6 +955,8 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { natvis_debugger_visualizers: Default::default(), lint_levels: CodegenLintLevels::from_tcx(tcx), metadata_symbol: exported_symbols::metadata_symbol_name(tcx), + each_linked_rlib_file_for_lto: Default::default(), + exported_symbols_for_lto: Default::default(), }; info.native_libraries.reserve(n_crates); @@ -1044,6 +1042,25 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { }); } + let mut each_linked_rlib_for_lto = Vec::new(); + let mut each_linked_rlib_file_for_lto = Vec::new(); + if tcx.sess.lto() != config::Lto::No && tcx.sess.lto() != config::Lto::ThinLocal { + drop(crate::back::link::each_linked_rlib(&info, None, &mut |cnum, path| { + if crate::back::link::ignored_for_lto(tcx.sess, &info, cnum) { + return; + } + + each_linked_rlib_for_lto.push(cnum); + each_linked_rlib_file_for_lto.push(path.to_path_buf()); + })); + } + info.each_linked_rlib_file_for_lto = each_linked_rlib_file_for_lto; + + // FIXME move to -Zlink-only half such that each_linked_rlib_file_for_lto can be moved there too + // Compute the set of symbols we need to retain when doing LTO (if we need to) + info.exported_symbols_for_lto = + crate::back::lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto); + let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => { // These are crate types for which we invoke the linker and can embed diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 22290d672c61..d48b54166270 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -95,19 +95,14 @@ pub fn into_compiled_module( emit_asm: bool, emit_ir: bool, outputs: &OutputFilenames, - invocation_temp: Option<&str>, ) -> CompiledModule { - let object = emit_obj - .then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp)); - let dwarf_object = - emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp)); - let bytecode = emit_bc - .then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp)); - let assembly = emit_asm - .then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp)); - let llvm_ir = emit_ir.then(|| { - outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp) - }); + let object = emit_obj.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name)); + let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name)); + let bytecode = emit_bc.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name)); + let assembly = + emit_asm.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name)); + let llvm_ir = + emit_ir.then(|| outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name)); CompiledModule { name: self.name, @@ -230,6 +225,8 @@ pub struct CrateInfo { pub natvis_debugger_visualizers: BTreeSet, pub lint_levels: CodegenLintLevels, pub metadata_symbol: String, + pub each_linked_rlib_file_for_lto: Vec, + pub exported_symbols_for_lto: Vec, } /// Target-specific options that get set in `cfg(...)`. diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 9a491c9c3eb1..45c6621ba013 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -518,6 +518,14 @@ pub(crate) fn codegen_rvalue_operand( self.codegen_place_to_pointer(bx, place, mk_ref) } + // Note: Exclusive reborrowing is always equal to a memcpy, as the types do not change. + // Generic shared reborrowing is not (necessarily) a simple memcpy, but currently the + // coherence check places such restrictions on the CoerceShared trait as to guarantee + // that it is. + mir::Rvalue::Reborrow(_, _, place) => { + self.codegen_operand(bx, &mir::Operand::Copy(place)) + } + mir::Rvalue::RawPtr(kind, place) => { let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy()) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7b95562ddda3..9898b67b91f7 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -104,7 +104,7 @@ fn provide(&self, _providers: &mut Providers) {} fn target_cpu(&self, sess: &Session) -> String; - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box; + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box; /// This is called on the returned `Box` from [`codegen_crate`](Self::codegen_crate) /// @@ -116,6 +116,7 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap); fn print_pass_timings(&self) {} diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index b66d69b92407..d7b18518aba9 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -610,6 +610,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { } } + Rvalue::Reborrow(..) => { + // FIXME(reborrow): figure out if this is relevant at all. + } + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => { // These are only inserted for slice length, so the place must already be indirect. // This implies we do not have to worry about whether the borrow escapes. diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 6f7fccdaf266..e1cab9af046b 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -252,6 +252,8 @@ pub fn in_rvalue<'tcx, Q, F>( in_place::(cx, in_local, place.as_ref()) } + Rvalue::Reborrow(_, _, place) => in_place::(cx, in_local, place.as_ref()), + Rvalue::WrapUnsafeBinder(op, _) => in_operand::(cx, in_local, op), Rvalue::Aggregate(kind, operands) => { diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 044b8b091b8d..a230f797b56f 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -191,6 +191,19 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { } } + mir::Rvalue::Reborrow(target, mutability, borrowed_place) => { + // A Reborrow allows mutation if it is Reborrow or if the CoerceShared target isn't + // Freeze. + if !borrowed_place.is_indirect() + && (mutability.is_mut() || !target.is_freeze(self.ccx.tcx, self.ccx.typing_env)) + { + if Q::in_any_value_of_ty(self.ccx, *target) { + self.state.qualif.insert(borrowed_place.local); + self.state.borrow.insert(borrowed_place.local); + } + } + } + mir::Rvalue::Cast(..) | mir::Rvalue::Use(..) | mir::Rvalue::CopyForDeref(..) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 50d58e020601..fefbf6adfd13 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use std::{fmt, mem}; -use rustc_abi::{Align, FIRST_VARIANT, Size}; +use rustc_abi::{Align, FIRST_VARIANT, FieldIdx, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -11,7 +11,7 @@ use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{self, FieldInfo, Ty, TyCtxt}; +use rustc_middle::ty::{self, FieldInfo, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::FnAbi; @@ -605,6 +605,23 @@ fn call_intrinsic( ecx.write_type_info(ty, dest)?; } + sym::size_of_type_id => { + let ty = ecx.read_type_id(&args[0])?; + let layout = ecx.layout_of(ty)?; + let variant_index = if layout.is_sized() { + let (variant, variant_place) = ecx.project_downcast_named(dest, sym::Some)?; + let size_field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?; + ecx.write_scalar( + ScalarInt::try_from_target_usize(layout.size.bytes(), ecx.tcx.tcx).unwrap(), + &size_field_place, + )?; + variant + } else { + ecx.project_downcast_named(dest, sym::None)?.0 + }; + ecx.write_discriminant(variant_index, dest)?; + } + sym::field_offset => { let frt_ty = instance.args.type_at(0); ensure_monomorphic_enough(ecx.tcx.tcx, frt_ty)?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 7b63ab5bb02e..3d8d9592459c 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; -use rustc_abi::{ExternAbi, FieldIdx, VariantIdx}; +use rustc_abi::{ExternAbi, FieldIdx}; use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; @@ -12,27 +12,11 @@ use crate::const_eval::CompileTimeMachine; use crate::interpret::{ - CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar, - Writeable, interp_ok, + CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable, + interp_ok, }; impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { - /// Equivalent to `project_downcast`, but identifies the variant by name instead of index. - fn downcast<'a>( - &self, - place: &(impl Writeable<'tcx, CtfeProvenance> + 'a), - name: Symbol, - ) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'a)> { - let variants = place.layout().ty.ty_adt_def().unwrap().variants(); - let variant_idx = variants - .iter_enumerated() - .find(|(_idx, var)| var.name == name) - .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) - .0; - - interp_ok((variant_idx, self.project_downcast(place, variant_idx)?)) - } - // A general method to write an array to a static slice place. fn allocate_fill_and_write_slice_ptr( &mut self, @@ -83,7 +67,7 @@ pub(crate) fn write_type_info( let variant_index = match ty.kind() { ty::Tuple(fields) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Tuple)?; + self.project_downcast_named(&field_dest, sym::Tuple)?; // project to the single tuple variant field of `type_info::Tuple` struct type let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; assert_eq!( @@ -102,7 +86,7 @@ pub(crate) fn write_type_info( } ty::Array(ty, len) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Array)?; + self.project_downcast_named(&field_dest, sym::Array)?; let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_array_type_info(array_place, *ty, *len)?; @@ -111,7 +95,7 @@ pub(crate) fn write_type_info( } ty::Slice(ty) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Slice)?; + self.project_downcast_named(&field_dest, sym::Slice)?; let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_slice_type_info(slice_place, *ty)?; @@ -123,16 +107,17 @@ pub(crate) fn write_type_info( } ty::Bool => { let (variant, _variant_place) = - self.downcast(&field_dest, sym::Bool)?; + self.project_downcast_named(&field_dest, sym::Bool)?; variant } ty::Char => { let (variant, _variant_place) = - self.downcast(&field_dest, sym::Char)?; + self.project_downcast_named(&field_dest, sym::Char)?; variant } ty::Int(int_ty) => { - let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?; + let (variant, variant_place) = + self.project_downcast_named(&field_dest, sym::Int)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_int_type_info( place, @@ -142,7 +127,8 @@ pub(crate) fn write_type_info( variant } ty::Uint(uint_ty) => { - let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?; + let (variant, variant_place) = + self.project_downcast_named(&field_dest, sym::Int)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_int_type_info( place, @@ -153,18 +139,19 @@ pub(crate) fn write_type_info( } ty::Float(float_ty) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Float)?; + self.project_downcast_named(&field_dest, sym::Float)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_float_type_info(place, float_ty.bit_width())?; variant } ty::Str => { - let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?; + let (variant, _variant_place) = + self.project_downcast_named(&field_dest, sym::Str)?; variant } ty::Ref(_, ty, mutability) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Reference)?; + self.project_downcast_named(&field_dest, sym::Reference)?; let reference_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_reference_type_info(reference_place, *ty, *mutability)?; @@ -173,7 +160,7 @@ pub(crate) fn write_type_info( } ty::RawPtr(ty, mutability) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Pointer)?; + self.project_downcast_named(&field_dest, sym::Pointer)?; let pointer_place = self.project_field(&variant_place, FieldIdx::ZERO)?; @@ -183,14 +170,14 @@ pub(crate) fn write_type_info( } ty::Dynamic(predicates, region) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::DynTrait)?; + self.project_downcast_named(&field_dest, sym::DynTrait)?; let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?; variant } ty::FnPtr(sig, fn_header) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::FnPtr)?; + self.project_downcast_named(&field_dest, sym::FnPtr)?; let fn_ptr_place = self.project_field(&variant_place, FieldIdx::ZERO)?; @@ -214,27 +201,10 @@ pub(crate) fn write_type_info( | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(..) - | ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0, + | ty::Error(_) => self.project_downcast_named(&field_dest, sym::Other)?.0, }; self.write_discriminant(variant_index, &field_dest)? } - sym::size => { - let layout = self.layout_of(ty)?; - let variant_index = if layout.is_sized() { - let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?; - let size_field_place = - self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_scalar( - ScalarInt::try_from_target_usize(layout.size.bytes(), self.tcx.tcx) - .unwrap(), - &size_field_place, - )?; - variant - } else { - self.downcast(&field_dest, sym::None)?.0 - }; - self.write_discriminant(variant_index, &field_dest)?; - } other => span_bug!(self.tcx.span, "unknown `Type` field {other}"), } } @@ -433,16 +403,17 @@ pub(crate) fn write_fn_ptr_type_info( sym::abi => match fn_sig_kind.abi() { ExternAbi::C { .. } => { let (rust_variant, _rust_place) = - self.downcast(&field_place, sym::ExternC)?; + self.project_downcast_named(&field_place, sym::ExternC)?; self.write_discriminant(rust_variant, &field_place)?; } ExternAbi::Rust => { let (rust_variant, _rust_place) = - self.downcast(&field_place, sym::ExternRust)?; + self.project_downcast_named(&field_place, sym::ExternRust)?; self.write_discriminant(rust_variant, &field_place)?; } other_abi => { - let (variant, variant_place) = self.downcast(&field_place, sym::Named)?; + let (variant, variant_place) = + self.project_downcast_named(&field_place, sym::Named)?; let str_place = self.allocate_str_dedup(other_abi.as_str())?; let str_ref = self.mplace_to_imm_ptr(&str_place, None)?; let payload = self.project_field(&variant_place, FieldIdx::ZERO)?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs index 2143313bbbad..9016064de8fd 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -22,7 +22,7 @@ pub(crate) fn write_adt_type_info( let (adt_ty, adt_def) = adt; let variant_idx = match adt_def.adt_kind() { AdtKind::Struct => { - let (variant, variant_place) = self.downcast(place, sym::Struct)?; + let (variant, variant_place) = self.project_downcast_named(place, sym::Struct)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_struct_type_info( place, @@ -32,7 +32,7 @@ pub(crate) fn write_adt_type_info( variant } AdtKind::Union => { - let (variant, variant_place) = self.downcast(place, sym::Union)?; + let (variant, variant_place) = self.project_downcast_named(place, sym::Union)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_union_type_info( place, @@ -42,7 +42,7 @@ pub(crate) fn write_adt_type_info( variant } AdtKind::Enum => { - let (variant, variant_place) = self.downcast(place, sym::Enum)?; + let (variant, variant_place) = self.project_downcast_named(place, sym::Enum)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_enum_type_info(place, adt, generics)?; variant @@ -219,13 +219,13 @@ fn write_generic_lifetime( _region: Region<'tcx>, place: MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let (variant_idx, _) = self.downcast(&place, sym::Lifetime)?; + let (variant_idx, _) = self.project_downcast_named(&place, sym::Lifetime)?; self.write_discriminant(variant_idx, &place)?; interp_ok(()) } fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> { - let (variant_idx, variant_place) = self.downcast(&place, sym::Type)?; + let (variant_idx, variant_place) = self.project_downcast_named(&place, sym::Type)?; let generic_type_place = self.project_field(&variant_place, FieldIdx::ZERO)?; for (field_idx, field_def) in generic_type_place @@ -251,7 +251,7 @@ fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpR fn write_generic_const(&mut self, c: Const<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> { let ConstKind::Value(c) = c.kind() else { bug!("expected a computed const, got {c:?}") }; - let (variant_idx, variant_place) = self.downcast(&place, sym::Const)?; + let (variant_idx, variant_place) = self.project_downcast_named(&place, sym::Const)?; let const_place = self.project_field(&variant_place, FieldIdx::ZERO)?; for (field_idx, field_def) in const_place diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index db72c02e308c..27f91b2b89b2 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, mir, span_bug, ty}; +use rustc_span::Symbol; use tracing::{debug, instrument}; use super::{ @@ -227,6 +228,22 @@ pub fn project_downcast>( base.offset(Size::ZERO, layout, self) } + /// Equivalent to `project_downcast`, but identifies the variant by name instead of index. + pub fn project_downcast_named>( + &self, + base: &P, + name: Symbol, + ) -> InterpResult<'tcx, (VariantIdx, P)> { + let variants = base.layout().ty.ty_adt_def().unwrap().variants(); + let variant_idx = variants + .iter_enumerated() + .find(|(_idx, var)| var.name == name) + .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) + .0; + + interp_ok((variant_idx, self.project_downcast(base, variant_idx)?)) + } + /// Compute the offset and field layout for accessing the given index. pub fn project_index>( &self, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 6aa2971c900c..8376d0499990 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -230,6 +230,11 @@ pub fn eval_rvalue_into_place( })?; } + Reborrow(_, _, place) => { + let op = self.eval_place_to_op(place, Some(dest.layout))?; + self.copy_op(&op, &dest)?; + } + RawPtr(kind, place) => { // Figure out whether this is an addr_of of an already raw place. let place_base_raw = if place.is_indirect_first_projection() { diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 17effa07e3bc..931317f80110 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -459,22 +459,6 @@ impl StableOrd for String { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } -impl ToStableHashKey for String { - type KeyType = String; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - self.clone() - } -} - -impl ToStableHashKey for (T1, T2) { - type KeyType = (T1::KeyType, T2::KeyType); - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Self::KeyType { - (self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx)) - } -} - impl StableHash for bool { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index f268192233c0..917b685958da 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -4,7 +4,6 @@ use rustc_ast as ast; use rustc_ast::NodeId; -use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, StableHash}; @@ -712,15 +711,6 @@ fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { } } -impl ToStableHashKey for Namespace { - type KeyType = Namespace; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Namespace { - *self - } -} - /// Just a helper ‒ separate structure for each namespace. #[derive(Copy, Clone, Default, Debug, StableHash)] pub struct PerNS { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 133efb77317b..36cf398dedb1 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -437,7 +437,7 @@ fn stable_hash(&self, _: &mut Hcx, hasher: &mut StableHashe // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); - CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); + CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(1); // Field representing types. FieldRepresentingType, sym::field_representing_type, field_representing_type, Target::Struct, GenericRequirement::Exact(3); diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 606a47bc4a22..77a9f3617f98 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,59 +1,7 @@ -use rustc_data_structures::stable_hasher::{ - StableHash, StableHashCtxt, StableHasher, ToStableHashKey, -}; -use rustc_span::def_id::DefPathHash; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use crate::HashIgnoredAttrId; -use crate::hir::{ - AttributeMap, BodyId, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, -}; -use crate::hir_id::ItemLocalId; - -impl ToStableHashKey for BodyId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) { - let BodyId { hir_id } = *self; - hir_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for TraitItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ImplItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ForeignItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} +use crate::hir::{AttributeMap, OwnerNodes}; // The following implementations of StableHash for `ItemId`, `TraitItemId`, and // `ImplItemId` deserve special attention. Normally we do not hash `NodeId`s within diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 7d606439cedc..d0bb68c5bc33 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -199,6 +199,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::sinf64 | sym::sinf128 | sym::size_of + | sym::size_of_type_id | sym::sqrtf16 | sym::sqrtf32 | sym::sqrtf64 @@ -281,6 +282,7 @@ pub(crate) fn check_intrinsic_type( let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]).skip_norm_wip(); (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }; + let type_id_ty = || tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let n_lts = 0; @@ -294,6 +296,7 @@ pub(crate) fn check_intrinsic_type( sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } + sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), @@ -313,16 +316,8 @@ pub(crate) fn check_intrinsic_type( sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), - sym::type_id => ( - 1, - 0, - vec![], - tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(), - ), - sym::type_id_eq => { - let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); - (0, 0, vec![type_id, type_id], tcx.types.bool) - } + sym::type_id => (1, 0, vec![], type_id_ty()), + sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), sym::type_id_vtable => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); @@ -335,17 +330,12 @@ pub(crate) fn check_intrinsic_type( let option_args = tcx.mk_args(&[dyn_ty.into()]); let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); - ( - 0, - 0, - vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2], - ret_ty, - ) + (0, 0, vec![type_id_ty(); 2], ret_ty) } sym::type_of => ( 0, 0, - vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap()], + vec![type_id_ty()], tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(), ), sym::offload => ( diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index ba22ee6d1aa8..0fb565b67d04 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -9,12 +9,13 @@ use rustc_hir::ItemKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::{self, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; -use rustc_infer::traits::Obligation; +use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; +use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::relate::solver_relating::RelateExt; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params, + self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, suggest_constraining_type_params, }; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -22,7 +23,7 @@ ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, }; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCause, ObligationCtxt}; use tracing::debug; use crate::errors; @@ -43,6 +44,8 @@ pub(super) fn check_trait<'tcx>( visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; + checker.check(lang_items.reborrow(), visit_implementation_of_reborrow)?; + checker.check(lang_items.coerce_shared(), visit_implementation_of_coerce_shared)?; checker .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; checker.check( @@ -259,6 +262,28 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E tcx.ensure_result().coerce_unsized_info(impl_did) } +fn visit_implementation_of_reborrow(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_reborrow: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + reborrow_info(tcx, impl_did) +} + +fn visit_implementation_of_coerce_shared(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_coerce_shared: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + coerce_shared_info(tcx, impl_did) +} + fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool { span.ctxt() .outer_expn_data() @@ -444,6 +469,327 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } } +fn structurally_normalize_ty<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + impl_did: LocalDefId, + span: Span, + ty: Unnormalized<'tcx, Ty<'tcx>>, +) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> { + let ocx = ObligationCtxt::new(infcx); + let Ok(normalized_ty) = ocx.structurally_normalize_ty( + &traits::ObligationCause::misc(span, impl_did), + tcx.param_env(impl_did), + ty, + ) else { + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + return None; + }; + let errors = ocx.try_evaluate_obligations(); + if !errors.is_empty() { + if infcx.next_trait_solver() { + unreachable!(); + } + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + debug!(?errors, "encountered errors while fulfilling"); + return None; + } + + Some((normalized_ty, ocx.into_pending_obligations())) +} + +pub(crate) fn reborrow_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + debug!("compute_reborrow_info(impl_did={:?})", impl_did); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let span = tcx.def_span(impl_did); + let trait_name = "Reborrow"; + + let reborrow_trait = tcx.require_lang_item(LangItem::Reborrow, span); + + let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); + let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); + + if trait_impl_lifetime_params_count(tcx, impl_did) != 1 { + return Err(tcx + .dcx() + .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name })); + } + + assert_eq!(trait_ref.def_id, reborrow_trait); + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let (def, args) = match source.kind() { + &ty::Adt(def, args) if def.is_struct() => (def, args), + _ => { + // Note: reusing error here as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + } + }; + + let lifetimes_count = generic_lifetime_params_count(args); + let data_fields = collect_struct_data_fields(tcx, def, args); + + if lifetimes_count != 1 { + let item = tcx.hir_expect_item(impl_did); + let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind { + of_trait.trait_ref.path.span + } else { + tcx.def_span(impl_did) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); + } + + if data_fields.is_empty() { + return Ok(()); + } + + // We've found some data fields. They must all be either be Copy or Reborrow. + for (field, span) in data_fields { + if assert_field_type_is_reborrow( + tcx, + &infcx, + reborrow_trait, + impl_did, + param_env, + field, + span, + ) + .is_ok() + { + // Field implements Reborrow. + return Ok(()); + } + + // Field does not implement Reborrow: it must be Copy. + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; + } + + Ok(()) +} + +fn assert_field_type_is_reborrow<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + reborrow_trait: DefId, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, +) -> Result<(), Vec>> { + if ty.ref_mutability() == Some(ty::Mutability::Mut) { + // Mutable references are Reborrow but not really. + return Ok(()); + } + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = + Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, reborrow_trait, [ty])); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { Err(errors) } else { Ok(()) } +} + +pub(crate) fn coerce_shared_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + debug!("compute_coerce_shared_info(impl_did={:?})", impl_did); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let span = tcx.def_span(impl_did); + let trait_name = "CoerceShared"; + + let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); + + let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); + let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); + + if trait_impl_lifetime_params_count(tcx, impl_did) != 1 { + return Err(tcx + .dcx() + .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name })); + } + + assert_eq!(trait_ref.def_id, coerce_shared_trait); + let Some((target, _obligations)) = structurally_normalize_ty( + tcx, + &infcx, + impl_did, + span, + Unnormalized::new_wip(trait_ref.args.type_at(1)), + ) else { + todo!("something went wrong with structurally_normalize_ty"); + }; + + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let data = match (source.kind(), target.kind()) { + (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) + if def_a.is_struct() && def_b.is_struct() => + { + // Check that both A and B have exactly one lifetime argument, and that they have the + // same number of data fields that is not more than 1. The eventual intention is to + // support multiple lifetime arguments (with the reborrowed lifetimes inferred from + // usage one way or another) and multiple data fields with B allowed to leave out fields + // from A. The current state is just the simplest choice. + let a_lifetimes_count = generic_lifetime_params_count(args_a); + let a_data_fields = collect_struct_data_fields(tcx, def_a, args_a); + let b_lifetimes_count = generic_lifetime_params_count(args_b); + let b_data_fields = collect_struct_data_fields(tcx, def_b, args_b); + + if a_lifetimes_count != 1 + || b_lifetimes_count != 1 + || a_data_fields.len() > 1 + || b_data_fields.len() > 1 + || a_data_fields.len() != b_data_fields.len() + { + let item = tcx.hir_expect_item(impl_did); + let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = + &item.kind + { + of_trait.trait_ref.path.span + } else { + tcx.def_span(impl_did) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); + } + + if a_data_fields.len() == 1 { + // We found one data field for both: we'll attempt to perform CoerceShared between + // them below. + let (a, span_a) = a_data_fields[0]; + let (b, span_b) = b_data_fields[0]; + + Some((a, b, coerce_shared_trait, span_a, span_b)) + } else { + // We found no data fields in either: this is a reborrowable marker type being + // coerced into a shared marker. That is fine too. + None + } + } + + _ => { + // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + } + }; + + // We've proven that we have two types with one lifetime each and 0 or 1 data fields each. + if let Some((source, target, trait_def_id, source_field_span, _target_field_span)) = data { + // struct Source(SourceData); + // struct Target(TargetData); + // + // 1 data field each; they must be the same type and Copy, or relate to one another using + // CoerceShared. + if source.ref_mutability() == Some(ty::Mutability::Mut) + && target.ref_mutability() == Some(ty::Mutability::Not) + && infcx + .eq_structurally_relating_aliases( + param_env, + source.peel_refs(), + target.peel_refs(), + source_field_span, + ) + .is_ok() + { + // &mut T implements CoerceShared to &T, except not really. + return Ok(()); + } + if infcx + .eq_structurally_relating_aliases(param_env, source, target, source_field_span) + .is_err() + { + // The two data fields don't agree on a common type; this means + // that they must be `A: CoerceShared`. Register an obligation + // for that. + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, trait_def_id, [source, target]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + } + // Finally, resolve all regions. + ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; + } else { + // Types match: check that it is Copy. + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, source, source_field_span)?; + } + } + + Ok(()) +} + +fn trait_impl_lifetime_params_count(tcx: TyCtxt<'_>, did: LocalDefId) -> usize { + tcx.generics_of(did) + .own_params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .count() +} + +fn generic_lifetime_params_count(args: &[ty::GenericArg<'_>]) -> usize { + args.iter().filter(|arg| arg.as_region().is_some()).count() +} + +fn collect_struct_data_fields<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::AdtDef<'tcx>, + args: ty::GenericArgsRef<'tcx>, +) -> Vec<(Ty<'tcx>, Span)> { + def.non_enum_variant() + .fields + .iter() + .filter_map(|f| { + // Ignore PhantomData fields + let ty = f.ty(tcx, args); + if ty.is_phantom_data() { + return None; + } + Some((ty, tcx.def_span(f.did))) + }) + .collect() +} + +fn assert_field_type_is_copy<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, +) -> Result<(), ErrorGuaranteed> { + let copy_trait = tcx.require_lang_item(LangItem::Copy, span); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = + Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, copy_trait, [ty])); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + Err(infcx.err_ctxt().report_fulfillment_errors(errors)) + } else { + Ok(()) + } +} + pub(crate) fn coerce_unsized_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 1bb54f27dcbd..e0528a3e3b49 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1358,6 +1358,24 @@ pub(crate) struct CoerceMulti { pub fields: MultiSpan, } +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires that a single lifetime parameter is passed between source and target" +)] +pub(crate) struct CoerceSharedNotSingleLifetimeParam { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag("implementing `{$trait_name}` does not allow multiple lifetimes or fields to be coerced")] +pub(crate) struct CoerceSharedMulti { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + #[derive(Diagnostic)] #[diag("the trait `{$trait_name}` may only be implemented for a coercion between structures", code = E0377)] pub(crate) struct CoerceUnsizedNonStruct { diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index 3bbd46f4844e..752ba4e8de39 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -176,22 +176,3 @@ impl StableOrd for ItemLocalId { HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; - -impl ToStableHashKey for HirId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) { - let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); - (def_path_hash, self.local_id) - } -} - -impl ToStableHashKey for ItemLocalId { - type KeyType = ItemLocalId; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> ItemLocalId { - *self - } -} diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index abd5f38f0ed0..9d9a42c13234 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -283,7 +283,8 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { } // Examine the target type and consider type-specific coercions, such - // as auto-borrowing, coercing pointer mutability, or pin-ergonomics. + // as auto-borrowing, coercing pointer mutability, pin-ergonomics, or + // generic reborrow. match *b.kind() { ty::RawPtr(_, b_mutbl) => { return self.coerce_to_raw_ptr(a, b, b_mutbl); @@ -297,6 +298,26 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { _ if let Some(to_pin_ref) = self.maybe_to_pin_ref(a, b) => { return self.coerce_to_pin_ref(to_pin_ref); } + ty::Adt(_, _) + if self.tcx.features().reborrow() + && self + .fcx + .infcx + .type_implements_trait( + self.tcx + .lang_items() + .reborrow() + .expect("Unexpectedly using core/std without reborrow"), + [b], + self.fcx.param_env, + ) + .must_apply_modulo_regions() => + { + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_reborrow(a, b)); + if reborrow_coerce.is_ok() { + return reborrow_coerce; + } + } _ => {} } @@ -320,6 +341,14 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { // It cannot convert closures that require unsafe. self.coerce_closure_to_fn(a, b) } + ty::Adt(_, _) if self.tcx.features().reborrow() => { + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_shared_reborrow(a, b)); + if reborrow_coerce.is_ok() { + reborrow_coerce + } else { + self.unify(a, b, ForceLeakCheck::No) + } + } _ => { // Otherwise, just use unification rules. self.unify(a, b, ForceLeakCheck::No) @@ -934,6 +963,74 @@ fn coerce_to_pin_ref( Ok(coerce) } + /// Applies generic exclusive reborrowing on type implementing `Reborrow`. + #[instrument(skip(self), level = "trace")] + fn coerce_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for reborrow. + let (ty::Adt(a_def, _), ty::Adt(b_def, _)) = (a.kind(), b.kind()) else { + return Err(TypeError::Mismatch); + }; + if a_def.did() == b_def.did() { + // Reborrow is applicable here + self.unify_and( + a, + b, + [], + Adjust::GenericReborrow(ty::Mutability::Mut), + ForceLeakCheck::No, + ) + } else { + // FIXME: CoerceShared check goes here, error for now + Err(TypeError::Mismatch) + } + } + + /// Applies generic exclusive reborrowing on type implementing `Reborrow`. + #[instrument(skip(self), level = "trace")] + fn coerce_shared_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for reborrow. + let (ty::Adt(a_def, _), ty::Adt(b_def, _)) = (a.kind(), b.kind()) else { + return Err(TypeError::Mismatch); + }; + if a_def.did() == b_def.did() { + // CoerceShared cannot be T -> T. + return Err(TypeError::Mismatch); + } + let Some(coerce_shared_trait_did) = self.tcx.lang_items().coerce_shared() else { + return Err(TypeError::Mismatch); + }; + let coerce_shared_trait_ref = ty::TraitRef::new(self.tcx, coerce_shared_trait_did, [a, b]); + let obligation = traits::Obligation::new( + self.tcx, + ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(coerce_shared_trait_ref), + ); + let ocx = ObligationCtxt::new(&self.infcx); + ocx.register_obligation(obligation); + let errs = ocx.evaluate_obligations_error_on_ambiguity(); + if errs.is_empty() { + Ok(InferOk { + value: ( + vec![Adjustment { + kind: Adjust::GenericReborrow(ty::Mutability::Not), + target: b, + }], + b, + ), + obligations: ocx.into_pending_obligations(), + }) + } else { + Err(TypeError::Mismatch) + } + } + fn coerce_from_fn_pointer( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a6129d97a328..010f96f85a22 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -751,6 +751,15 @@ fn walk_adjustment(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { adjustment::Adjust::Borrow(ref autoref) => { self.walk_autoref(expr, &place_with_id, autoref); } + + adjustment::Adjust::GenericReborrow(_reborrow) => { + // To build an expression as a place expression, it needs to be a field + // projection or deref at the outmost layer. So it is field projection or deref + // on an adjusted value. But this means that adjustment is applied on a + // subexpression that is not the final operand/rvalue for function call or + // assignment. This is a contradiction. + unreachable!("Reborrow trait usage during adjustment walk"); + } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; } @@ -1282,7 +1291,8 @@ fn cat_expr_adjusted_with( adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) => { + | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::GenericReborrow(..) => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index eae0cb5cb4e2..6dd6741b48ea 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -343,6 +343,9 @@ pub(crate) fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec { // FIXME(const_trait_impl): We should probably enforce these. } + Adjust::GenericReborrow(_) => { + // FIXME(reborrow): figure out if we have effects to enforce here. + } Adjust::Borrow(_) => { // No effects to enforce here. } diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index eaf9c62c954f..243ac20ee441 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -362,6 +362,116 @@ fn chunked_bitset() { ); } +/// Additional helper methods for testing. +impl ChunkedBitSet { + /// Creates a new `ChunkedBitSet` containing all `i` for which `fill_fn(i)` is true. + fn fill_with(domain_size: usize, fill_fn: impl Fn(usize) -> bool) -> Self { + let mut this = ChunkedBitSet::new_empty(domain_size); + for i in 0..domain_size { + if fill_fn(i) { + this.insert(i); + } + } + this + } + + /// Asserts that for each `i` in `0..self.domain_size()`, `self.contains(i) == expected_fn(i)`. + #[track_caller] + fn assert_filled_with(&self, expected_fn: impl Fn(usize) -> bool) { + for i in 0..self.domain_size() { + let expected = expected_fn(i); + assert_eq!(self.contains(i), expected, "i = {i}"); + } + } +} + +#[test] +fn chunked_bulk_ops() { + struct ChunkedBulkOp { + name: &'static str, + op_fn: fn(&mut ChunkedBitSet, &ChunkedBitSet) -> bool, + spec_fn: fn(fn(usize) -> bool, fn(usize) -> bool, usize) -> bool, + } + let ops = &[ + ChunkedBulkOp { + name: "union", + op_fn: ChunkedBitSet::union, + spec_fn: |fizz, buzz, i| fizz(i) || buzz(i), + }, + ChunkedBulkOp { + name: "subtract", + op_fn: ChunkedBitSet::subtract, + spec_fn: |fizz, buzz, i| fizz(i) && !buzz(i), + }, + ChunkedBulkOp { + name: "intersect", + op_fn: ChunkedBitSet::intersect, + spec_fn: |fizz, buzz, i| fizz(i) && buzz(i), + }, + ]; + + let domain_sizes = [ + CHUNK_BITS / 7, // Smaller than a full chunk. + CHUNK_BITS, + (CHUNK_BITS + CHUNK_BITS / 7), // Larger than a full chunk. + ]; + + for ChunkedBulkOp { name, op_fn, spec_fn } in ops { + for domain_size in domain_sizes { + // If false, use different values for LHS and RHS, to test "fizz op buzz". + // If true, use identical values, to test "fizz op fizz". + for identical in [false, true] { + // If false, make a clone of LHS before doing the op. + // This covers optimizations that depend on whether chunk words are shared or not. + for unique in [false, true] { + // Print the current test case, so that we can see which one failed. + println!( + "Testing op={name}, domain_size={domain_size}, identical={identical}, unique={unique} ..." + ); + + let fizz_fn = |i| i % 3 == 0; + let buzz_fn = if identical { fizz_fn } else { |i| i % 5 == 0 }; + + // Check that `fizz op buzz` gives the expected results. + chunked_bulk_ops_test_inner( + domain_size, + unique, + fizz_fn, + buzz_fn, + op_fn, + |i| spec_fn(fizz_fn, buzz_fn, i), + ); + } + } + } + } +} + +fn chunked_bulk_ops_test_inner( + domain_size: usize, + unique: bool, + fizz_fn: impl Fn(usize) -> bool + Copy, + buzz_fn: impl Fn(usize) -> bool + Copy, + op_fn: impl Fn(&mut ChunkedBitSet, &ChunkedBitSet) -> bool, + expected_fn: impl Fn(usize) -> bool + Copy, +) { + // Create two bitsets, "fizz" (LHS) and "buzz" (RHS). + let mut fizz = ChunkedBitSet::fill_with(domain_size, fizz_fn); + let buzz = ChunkedBitSet::fill_with(domain_size, buzz_fn); + + // If requested, clone `fizz` so that its word Rcs are not uniquely-owned. + let _cloned = (!unique).then(|| fizz.clone()); + + // Perform the op (e.g. union/subtract/intersect), and verify that the + // mutated LHS contains exactly the expected values. + let changed = op_fn(&mut fizz, &buzz); + fizz.assert_filled_with(expected_fn); + + // Verify that the "changed" return value is correct. + let should_change = (0..domain_size).any(|i| fizz_fn(i) != expected_fn(i)); + assert_eq!(changed, should_change); +} + fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSet { let mut s = ChunkedBitSet::new_empty(domain_size); for &e in elements { diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index c234e21b9254..9c115736a3d4 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index ab8bc1c7f1b3..875ed4ae5d30 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -385,7 +385,9 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se trace!("run_compiler"); // Set parallel mode before thread pool creation, which will create `Lock`s. - rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1); + rustc_data_structures::sync::set_dyn_thread_safe_mode( + config.opts.unstable_opts.threads.is_some(), + ); // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); @@ -407,7 +409,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se util::run_in_thread_pool_with_globals( &early_dcx, config.opts.edition, - config.opts.unstable_opts.threads, + config.opts.unstable_opts.threads.unwrap_or(1), &config.extra_symbols, SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }, |current_gcx, jobserver_proxy| { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index cb41974af41b..bcd1a52ce9dc 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1305,8 +1305,6 @@ pub(crate) fn start_codegen<'tcx>( let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); - let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); - let codegen = tcx.sess.time("codegen_crate", || { if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { // Skip crate items and just output metadata in -Z no-codegen mode. @@ -1315,7 +1313,7 @@ pub(crate) fn start_codegen<'tcx>( // Linker::link will skip join_codegen in case of a CodegenResults Any value. Box::new(CompiledModules { modules: vec![], allocator_module: None }) } else { - codegen_backend.codegen_crate(tcx, &crate_info) + codegen_backend.codegen_crate(tcx) } }); @@ -1327,6 +1325,8 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } + let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); + (codegen, crate_info, metadata) } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 63c7332893b8..fd1b5104fc6d 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -53,9 +53,12 @@ pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { // This was a check only build Ok(compiled_modules) => (*compiled_modules, IndexMap::default()), - Err(ongoing_codegen) => { - codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames) - } + Err(ongoing_codegen) => codegen_backend.join_codegen( + ongoing_codegen, + sess, + &self.output_filenames, + &self.crate_info, + ), } }); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index a7e0dd2ac39c..83930bf1249a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -734,7 +734,7 @@ macro_rules! untracked { untracked!(span_debug, true); untracked!(span_free_formats, true); untracked!(temps_dir, Some(String::from("abc"))); - untracked!(threads, 99); + untracked!(threads, Some(99)); untracked!(time_llvm_passes, true); untracked!(time_passes, true); untracked!(time_passes_format, TimePassesFormat::Json); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 24b23cc4199e..d7d306918fd0 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, OnceLock}; use std::{env, thread}; +use rand::{RngCore, rng}; use rustc_ast as ast; use rustc_attr_parsing::ShouldEmit; use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; @@ -12,6 +13,7 @@ use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; +use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; @@ -406,7 +408,7 @@ fn target_cpu(&self, _sess: &Session) -> String { String::new() } - fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box { Box::new(CompiledModules { modules: vec![], allocator_module: None }) } @@ -415,6 +417,7 @@ fn join_codegen( ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { (*ongoing_codegen.downcast().unwrap(), FxIndexMap::default()) } @@ -615,6 +618,12 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string()) }); + let invocation_temp = sess + .opts + .incremental + .as_ref() + .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); + match sess.io.output_file { None => { // "-" as input file will cause the parser to read from stdin so we @@ -631,6 +640,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu stem, None, sess.io.temps_dir.clone(), + invocation_temp, sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), @@ -661,6 +671,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu out_filestem, ofile, sess.io.temps_dir.clone(), + invocation_temp, sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 9a374488ab6f..24759f3ee4a0 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -173,6 +173,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) + | Adjust::GenericReborrow(..) | Adjust::Borrow(AutoBorrow::RawPtr(..) | AutoBorrow::Pin(..)) => None, } } diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index bb047e9782d4..f45c24280083 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -138,17 +138,16 @@ USES_POWER_ALIGNMENT ]); -/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple). -#[inline] -fn get_type_from_field<'tcx>( +/// A common pattern in this lint is to attempt normalize_erasing_regions, +/// but keep the original type if it were to fail. +/// This may or may not be supported in the logic behind the `Unnormalized` wrapper, +/// (FIXME?) +/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be. +fn maybe_normalize_erasing_regions<'tcx>( cx: &LateContext<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, + value: Unnormalized<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { - let field_ty = field.ty(cx.tcx, args); - cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty)) - .unwrap_or(field_ty) + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip()) } /// Check a variant of a non-exhaustive enum for improper ctypes @@ -257,12 +256,22 @@ fn check_struct_for_power_alignment<'tcx>( } } +/// Annotates whether we are in the context of an item *defined* in rust +/// and exposed to an FFI boundary, +/// or the context of an item from elsewhere, whose interface is re-*declared* in rust. #[derive(Clone, Copy)] enum CItemKind { Declaration, Definition, } +/// Annotates whether we are in the context of a function's argument types or return type. +#[derive(Clone, Copy)] +enum FnPos { + Arg, + Ret, +} + enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -286,8 +295,10 @@ enum IndirectionKind { } bitflags! { + /// VisitorState flags that are linked with the root type's use. + /// (These are the permanent part of the state, kept when visiting new Ty.) #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct VisitorState: u8 { + struct RootUseFlags: u8 { /// For use in (externally-linked) static variables. const STATIC = 0b000001; /// For use in functions in general. @@ -302,7 +313,45 @@ struct VisitorState: u8 { } } -impl VisitorState { +/// Description of the relationship between current Ty and +/// the type (or lack thereof) immediately containing it +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum OuterTyKind { + None, + /// A variant that should not exist, + /// but is needed because we don't change the lint's behavior yet + NoneThroughFnPtr, + /// Placeholder for properties that will be used eventually + Other, +} + +impl OuterTyKind { + /// Computes the relationship by providing the containing Ty itself + fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { + match ty.kind() { + ty::FnPtr(..) => Self::NoneThroughFnPtr, + ty::RawPtr(..) + | ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(..) + | ty::Array(..) + | ty::Slice(_) => OuterTyKind::Other, + _ => bug!("Unexpected outer type {ty:?}"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct VisitorState { + /// Flags describing both the overall context in which the current Ty is, + /// linked to how the Visitor's original Ty was used. + root_use_flags: RootUseFlags, + /// Flags describing both the immediate context in which the current Ty is, + /// linked to how it relates to its parent Ty (or lack thereof). + outer_ty_kind: OuterTyKind, +} + +impl RootUseFlags { // The values that can be set. const STATIC_TY: Self = Self::STATIC; const ARGUMENT_TY_IN_DEFINITION: Self = @@ -317,86 +366,85 @@ impl VisitorState { const RETURN_TY_IN_FNPTR: Self = Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits()) .unwrap(); +} - /// Get the proper visitor state for a given function's arguments. - fn argument_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION, +impl VisitorState { + /// From an existing state, compute the state of any subtype of the current type. + /// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.) + fn next(&self, current_ty: Ty<'_>) -> Self { + assert!(!matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: self.root_use_flags, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } - /// Get the proper visitor state for a given function's return type. - fn return_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION, + /// From an existing state, compute the state of any subtype of the current type. + /// (Case where the current type is a function pointer, + /// meaning we need to specify if the subtype is an argument or the return.) + fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self { + assert!(matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: match fn_pos { + FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR, + FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, + }, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } + /// Get the proper visitor state for a given function's arguments or return type. + fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self { + let p_flags = match (fn_mode, fn_pos) { + (CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION, + (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, + }; + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + } + + /// Get the proper visitor state for a static variable's type + fn static_entry_point() -> Self { + VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + } + /// Whether the type is used in a function. - fn is_in_function(self) -> bool { - let ret = self.contains(Self::FUNC); + fn is_in_function(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FUNC); if ret { - debug_assert!(!self.contains(Self::STATIC)); + debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC)); } ret } + /// Whether the type is used (directly or not) in a function, in return position. - fn is_in_function_return(self) -> bool { - let ret = self.contains(Self::FN_RETURN); + fn is_in_function_return(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN); if ret { debug_assert!(self.is_in_function()); } ret } + /// Whether the type is used (directly or not) in a defined function. /// In other words, whether or not we allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_defined_function(self) -> bool { - self.contains(Self::DEFINED) && self.is_in_function() + fn is_in_defined_function(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function() } /// Whether the type is used (directly or not) in a function pointer type. /// Here, we also allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_fnptr(self) -> bool { - self.contains(Self::THEORETICAL) && self.is_in_function() + fn is_in_fnptr(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function() } /// Whether we can expect type parameters and co in a given type. - fn can_expect_ty_params(self) -> bool { + fn can_expect_ty_params(&self) -> bool { // rust-defined functions, as well as FnPtrs - self.contains(Self::THEORETICAL) || self.is_in_defined_function() - } -} - -bitflags! { - /// Data that summarises how an "outer type" surrounds its inner type(s) - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct OuterTyData: u8 { - /// To show that there is no outer type, the current type is directly used by a `static` - /// variable or a function/FnPtr - const NO_OUTER_TY = 0b01; - /// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically - /// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake - const NO_OUTER_TY_FNPTR = 0b10; - } -} - -impl OuterTyData { - /// Get the proper data for a given outer type. - fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { - match ty.kind() { - ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR, - ty::RawPtr(..) - | ty::Ref(..) - | ty::Adt(..) - | ty::Tuple(..) - | ty::Array(..) - | ty::Slice(_) => Self::empty(), - k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k), - } + self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function() } } @@ -415,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + fn new( + cx: &'a LateContext<'tcx>, + base_ty: Unnormalized<'tcx, Ty<'tcx>>, + base_fn_mode: CItemKind, + ) -> Self { + ImproperCTypesVisitor { + cx, + base_ty: maybe_normalize_erasing_regions(cx, base_ty), + base_fn_mode, + cache: FxHashSet::default(), + } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -485,7 +542,7 @@ fn visit_indirection( { FfiSafe } else { - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } } @@ -505,8 +562,11 @@ fn visit_variant_fields( let transparent_with_all_zst_fields = if def.repr().transparent() { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. - let field_ty = get_type_from_field(self.cx, field, args); - match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); + match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, } @@ -524,8 +584,11 @@ fn visit_variant_fields( // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - let field_ty = get_type_from_field(self.cx, field, args); - all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); + all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, @@ -570,7 +633,7 @@ fn visit_struct_or_union( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct" )) } else { - // FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises + // FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises Some(msg!( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union" )) @@ -628,7 +691,7 @@ fn visit_enum( if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option` and `Result` if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) { - return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty); + return self.visit_type(state.next(ty), inner_ty); } return FfiUnsafe { @@ -660,12 +723,7 @@ fn visit_enum( /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). - fn visit_type( - &mut self, - state: VisitorState, - outer_ty: OuterTyData, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { + fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; let tcx = self.cx.tcx; @@ -709,7 +767,8 @@ fn visit_type( // Pattern types are just extra invariants on the type that you need to uphold, // but only the base type is relevant for being representable in FFI. // (note: this lint was written when pattern types could only be integers constrained to ranges) - ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty), + // (also note: the lack of ".next(ty)" on the state is on purpose) + ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty), // types which likely have a stable representation, if the target architecture defines those // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64 @@ -740,12 +799,14 @@ fn visit_type( }, ty::Tuple(tuple) => { - // C functions can return void - let empty_and_safe = tuple.is_empty() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - && state.is_in_function_return(); - - if empty_and_safe { + if tuple.is_empty() + && state.is_in_function_return() + && matches!( + state.outer_ty_kind, + OuterTyKind::None | OuterTyKind::NoneThroughFnPtr + ) + { + // C functions can return void FfiSafe } else { FfiUnsafe { @@ -774,9 +835,8 @@ fn visit_type( ty::Array(inner_ty, _) => { if state.is_in_function() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - // FIXME(ctypes): VVV-this-VVV shouldn't be the case - && !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR) + // FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr + && matches!(state.outer_ty_kind, OuterTyKind::None) { // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. @@ -788,7 +848,7 @@ fn visit_type( } else { // let's allow phantoms to go through, // since an array of 1-ZSTs is also a 1-ZST - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } @@ -806,19 +866,14 @@ fn visit_type( let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { - match self.visit_type( - VisitorState::ARGUMENT_TY_IN_FNPTR, - OuterTyData::from_ty(ty), - *arg, - ) { + match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) { FfiSafe => {} r => return r, } } let ret_ty = sig.output(); - - self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty) + self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty) } ty::Foreign(..) => FfiSafe, @@ -886,17 +941,17 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { }) } - fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) - .unwrap_or(ty); + fn check_type( + &mut self, + state: VisitorState, + ty: Unnormalized<'tcx, Ty<'tcx>>, + ) -> FfiResult<'tcx> { + let ty = maybe_normalize_erasing_regions(self.cx, ty); if let Some(res) = self.visit_for_opaque_ty(ty) { return res; } - self.visit_type(state, OuterTyData::NO_OUTER_TY, ty) + self.visit_type(state, ty) } } @@ -925,7 +980,7 @@ fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { self.spans.push(ty.span); } - hir::intravisit::walk_ty(self, ty) + hir::intravisit::walk_ty(self, ty); } } @@ -949,6 +1004,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); for (fn_ptr_ty, span) in all_types { + let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); // FIXME(ctypes): make a check_for_fnptr let ffi_res = visitor.check_type(state, fn_ptr_ty); @@ -970,12 +1026,12 @@ fn check_fn_for_external_abi_fnptr( let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode); } } @@ -998,9 +1054,9 @@ fn check_reprc_adt( } fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { - let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); + let ty = cx.tcx.type_of(id).instantiate_identity(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); - let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty); + let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); } @@ -1016,16 +1072,18 @@ fn check_foreign_fn( let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); - let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); - let ffi_res = visitor.check_type(state, *input_ty); + let input_ty = Unnormalized::new_wip(*input_ty); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); + let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); + let ffi_res = visitor.check_type(state, input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); - let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); - let ffi_res = visitor.check_type(state, sig.output()); + let output_ty = Unnormalized::new_wip(sig.output()); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); + let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); + let ffi_res = visitor.check_type(state, output_ty); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); } } @@ -1124,7 +1182,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { | hir::ItemKind::TyAlias(_, _, ty) => { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), ty, cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, @@ -1158,7 +1216,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), field.ty, cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 517f2954c546..025f36a34f00 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,12 +3,11 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ - StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + StableCompare, StableHash, StableHashCtxt, StableHasher, }; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_hir_id::{HirId, ItemLocalId}; +use rustc_hir_id::HirId; use rustc_macros::{Decodable, Encodable, StableHash}; -use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; use rustc_span::{AttrId, Ident, Symbol, sym}; use serde::{Deserialize, Serialize}; @@ -154,23 +153,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl ToStableHashKey for LintExpectationId { - type KeyType = (DefPathHash, ItemLocalId, u16, u16); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Self::KeyType { - match self { - LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { - let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx); - (def_path_hash, lint_idx, *attr_index, *lint_index) - } - _ => { - unreachable!("StableHash should only be called for a filled `LintExpectationId`") - } - } - } -} - /// Setting for how to handle a lint. /// /// See: @@ -623,15 +605,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl ToStableHashKey for LintId { - type KeyType = &'static str; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> &'static str { - self.lint_name_raw() - } -} - impl StableCompare for LintId { const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a00fb59963ac..cbd6afd68473 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -261,7 +261,8 @@ fn into_args(self) -> (DefId, SimplifiedType) { .coerce_unsized_info .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } + .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) + } mir_const_qualif => { table } rendered_const => { table } rendered_precise_capturing_args => { table } @@ -300,10 +301,10 @@ fn into_args(self) -> (DefId, SimplifiedType) { Ok(cdata .root .tables - .trait_impl_trait_tys + .collect_return_position_impl_trait_in_trait_tys .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) + .process_decoded(tcx, || panic!("{def_id:?} does not have collect_return_position_impl_trait_in_trait_tys"))) } associated_types_for_impl_traits_in_trait_or_impl => { table } @@ -695,6 +696,7 @@ impl CrateStore for CStore { fn as_any(&self) -> &dyn Any { self } + fn untracked_as_any(&mut self) -> &mut dyn Any { self } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 729a0dda7cf3..a0db004b7f4c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1628,7 +1628,7 @@ fn encode_def_ids(&mut self) { if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.trait_impl_trait_tys[def_id] <- table); + record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); @@ -2465,7 +2465,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { return; }; - if tcx.sess.threads() != 1 { + if tcx.sess.threads().is_some() { // Prefetch some queries used by metadata encoding. // This is not necessary for correctness, but is only done for performance reasons. // It can be removed if it turns out to cause trouble or be detrimental to performance. diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c7b2eaa15ebf..a3645a5556bf 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -466,7 +466,7 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables { macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, - trait_impl_trait_tys: Table>>>>, + collect_return_position_impl_trait_in_trait_tys: Table>>>>, doc_link_resolutions: Table>, doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index de6a105ee2b7..e12583f38fe7 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -1,7 +1,7 @@ /// This higher-order macro declares a list of types which can be allocated by `Arena`. /// /// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. +/// listed. See the `impl_arena_allocatable_decoder!` macro for more. #[macro_export] macro_rules! arena_types { ($macro:path) => ( @@ -9,8 +9,7 @@ macro_rules! arena_types { [] layout: rustc_abi::LayoutData, [] proxy_coroutine_layout: rustc_middle::mir::CoroutineLayout<'tcx>, [] fn_abi: rustc_target::callconv::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, - // AdtDef are interned and compared by address - [decode] adt_def: rustc_middle::ty::AdtDefData, + [] adt_def: rustc_middle::ty::AdtDefData, [] steal_thir: rustc_data_structures::steal::Steal>, [] steal_mir: rustc_data_structures::steal::Steal>, [decode] mir: rustc_middle::mir::Body<'tcx>, @@ -27,7 +26,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_data_structures::fx::FxIndexMap< + [] borrowck_result: rustc_data_structures::fx::FxIndexMap< rustc_hir::def_id::LocalDefId, rustc_middle::ty::DefinitionSiteHiddenType<'tcx>, >, @@ -100,7 +99,7 @@ macro_rules! arena_types { // (during lowering) and the `rustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::unord::UnordSet, - [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, + [] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind_vtable: rustc_middle::dep_graph::DepKindVTable<'tcx>, @@ -111,7 +110,7 @@ macro_rules! arena_types { rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, - [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, + [] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 38ceac7ae5bd..60a4b66d3918 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -51,7 +51,7 @@ use std::hash::Hash; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::DefPathHash; use rustc_macros::{Decodable, Encodable, StableHash}; @@ -231,13 +231,7 @@ pub fn from_cgu_name(cgu_name: &str) -> WorkProductId { WorkProductId { hash: hasher.finish() } } } -impl ToStableHashKey for WorkProductId { - type KeyType = Fingerprint; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - self.hash - } -} + impl StableOrd for WorkProductId { // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index a219809541cc..cc007d35c219 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -629,7 +629,7 @@ fn assert_dep_node_not_yet_allocated_in_current_session( let ok = match color { DepNodeColor::Unknown => true, DepNodeColor::Red => false, - DepNodeColor::Green(..) => sess.threads() > 1, // Other threads may mark this green + DepNodeColor::Green(..) => sess.threads().is_some(), // Other threads may mark this green }; if !ok { panic!("{}", msg()) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 380ba959c25f..e1bc29d5c368 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1128,6 +1128,14 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { write!(fmt, "&{region}{kind_str}{place:?}") } + Reborrow(target, mutability, ref place) => { + write!( + fmt, + "{target:?}({} {place:?})", + if mutability.is_mut() { "reborrow" } else { "coerce shared" } + ) + } + CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"), RawPtr(mutability, ref place) => { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index c5945848baad..c1f1b398ac14 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -766,6 +766,7 @@ pub fn is_safe_to_remove(&self) -> bool { | Rvalue::CopyForDeref(_) | Rvalue::Repeat(_, _) | Rvalue::Ref(_, _, _) + | Rvalue::Reborrow(_, _, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) | Rvalue::Cast( @@ -790,6 +791,12 @@ pub fn is_safe_to_remove(&self) -> bool { } } + /// Returns true if rvalue is a generic Reborrow coercion (usage of Reborrow or CoerceShared + /// trait). + pub fn is_generic_reborrow(&self) -> bool { + matches!(self, Self::Reborrow(..)) + } + pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where D: ?Sized + HasLocalDecls<'tcx>, @@ -804,6 +811,7 @@ pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } + Rvalue::Reborrow(target, _, _) => target, Rvalue::RawPtr(kind, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0eeefd4060be..8b015e6cecaa 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1461,6 +1461,25 @@ pub enum Rvalue<'tcx> { /// Wraps a value in an unsafe binder. WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), + + /// Creates a bitwise copy of the indicated place with the same type (if Mut) or its + /// CoerceShared target type (if Not). The type is known to be an ADT with exactly one lifetime + /// parameter, and it is known to implement the Reborrow trait (for Mut), and the CoerceShared + /// trait (only if Not). The CoerceShared target type is known to also have exactly one lifetime + /// parameter, implement Copy and (currently) have the same memory layout as the source type. + /// + /// The borrow checker uses the single lifetime in the source and target types to create a + /// Covariant outlives-bound between the source and target with the Mutability of the Reborrow. + /// This makes accessing the source value for writes (and reads if Mut) for the lifetime of the + /// target value a borrow check error, imitating `&mut T` and `&T`'s reborrowing on user ADTs. + /// + /// Future work may add support for multiple lifetimes and changing memory layout as part of + /// CoerceShared. These may be end up implemented as multiple MIR operations. + /// + /// This is produced by the [`ExprKind::Reborrow`]. + /// + /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow + Reborrow(Ty<'tcx>, Mutability, Place<'tcx>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, StableHash)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index c660c77cd84a..d73eac59dfb5 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -718,6 +718,18 @@ fn super_rvalue( self.visit_place(path, ctx, location); } + Rvalue::Reborrow(target, mutability, place) => { + self.visit_ty($(& $mutability)? *target, TyContext::Location(location)); + self.visit_place( + place, + match mutability { + Mutability::Not => PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow), + Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::Borrow), + }, + location + ); + } + Rvalue::CopyForDeref(place) => { self.visit_place( place, @@ -802,6 +814,8 @@ fn super_rvalue( self.visit_operand(op, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + + } } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 5c92f126e116..71bd08861b8d 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -5,6 +5,7 @@ use std::hash::Hash; use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stable_hasher::StableHash; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::OwnerId; @@ -256,8 +257,8 @@ fn default_span(&self, _: TyCtxt<'_>) -> Span { } impl<'tcx> QueryKey for Ty<'tcx> { - fn default_span(&self, _: TyCtxt<'_>) -> Span { - DUMMY_SP + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + def_id_of_type(*self).map(|def_id| tcx.def_span(def_id)).unwrap_or(DUMMY_SP) } } @@ -360,3 +361,58 @@ fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } + +/// Gets a `DefId` associated with a type +/// +/// Visited set is needed to avoid full iteration over +/// deeply nested tuples that have no DefId. +fn def_id_of_type_cached<'a>(ty: Ty<'a>, visited: &mut SsoHashSet>) -> Option { + match *ty.kind() { + ty::Adt(adt_def, _) => Some(adt_def.did()), + + ty::Dynamic(data, ..) => data.principal_def_id(), + + ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => { + def_id_of_type_cached(subty, visited) + } + + ty::RawPtr(ty, _) => def_id_of_type_cached(ty, visited), + + ty::Ref(_, ty, _) => def_id_of_type_cached(ty, visited), + + ty::Tuple(tys) => tys.iter().find_map(|ty| { + if visited.insert(ty) { + return def_id_of_type_cached(ty, visited); + } + return None; + }), + + ty::FnDef(def_id, _) + | ty::Closure(def_id, _) + | ty::CoroutineClosure(def_id, _) + | ty::Coroutine(def_id, _) + | ty::CoroutineWitness(def_id, _) + | ty::Foreign(def_id) => Some(def_id), + + ty::Alias(alias) => Some(alias.kind.def_id()), + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Str + | ty::FnPtr(..) + | ty::UnsafeBinder(_) + | ty::Placeholder(..) + | ty::Param(_) + | ty::Infer(_) + | ty::Bound(..) + | ty::Error(_) + | ty::Never + | ty::Float(_) => None, + } +} + +fn def_id_of_type(ty: Ty<'_>) -> Option { + def_id_of_type_cached(ty, &mut SsoHashSet::new()) +} diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 887eb3d88890..0cc57e5021f8 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -546,6 +546,18 @@ pub enum ExprKind<'tcx> { Yield { value: ExprId, }, + /// Use of an ADT that implements the Reborrow (for Mut) or CoerceShared traits (for Not). This + /// expression is produced by the [`Adjust::GenericReborrow`] in places where normally the ADT + /// would be moved or assigned over. Instead, this produces an [`Rvalue::Reborrow`] which + /// produces a bitwise copy of the source ADT and disables the source for the copy's lifetime. + /// + /// [`Adjust::GenericReborrow`]: crate::ty::adjustment::Adjust::GenericReborrow + /// [`Rvalue::Reborrow`]: mir::Rvalue::Reborrow + Reborrow { + source: ExprId, + mutability: Mutability, + target: Ty<'tcx>, + }, } /// Represents the association of a field identifier and an expression. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index aa1b6b1663bf..3361e43b15dd 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -187,6 +187,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), + Reborrow { .. } => {} } } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 7d4f56d89c7a..7174427e517d 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -103,6 +103,14 @@ pub enum Adjust { Borrow(AutoBorrow), Pointer(PointerCoercion), + + /// Take a user-type T implementing the Reborrow trait (for Mut) or the CoerceShared trait (for + /// Not) and reborrow as `T` or `CoreceShared`. + /// + /// This produces an [`ExprKind::Reborrow`]. + /// + /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow + GenericReborrow(hir::Mutability), } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 28bdeabf34dc..8b1e81258278 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -513,9 +513,8 @@ fn $name(&mut self) -> $ty { } macro_rules! impl_arena_allocatable_decoder { - ([]$args:tt) => {}; - ([decode $(, $attrs:ident)*] - [$name:ident: $ty:ty]) => { + ([] $name:ident: $ty:ty) => {}; + ([decode] $name:ident: $ty:ty) => { impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { @@ -535,7 +534,7 @@ fn decode(decoder: &mut D) -> &'tcx Self { macro_rules! impl_arena_allocatable_decoders { ([$($a:tt $name:ident: $ty:ty,)*]) => { $( - impl_arena_allocatable_decoder!($a [$name: $ty]); + impl_arena_allocatable_decoder!($a $name: $ty); )* } } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index bacddb680829..1ab8aa702752 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, Unnormalized, search_graph}; use crate::dep_graph::{DepKind, DepNodeIndex}; @@ -39,6 +39,20 @@ fn next_trait_solver_globally(self) -> bool { type AdtId = DefId; type ImplId = DefId; type UnevaluatedConstId = DefId; + type TraitAssocTyId = DefId; + type TraitAssocConstId = DefId; + type TraitAssocTermId = DefId; + type OpaqueTyId = DefId; + type LocalOpaqueTyId = LocalDefId; + type FreeTyAliasId = DefId; + type FreeConstAliasId = DefId; + type FreeTermAliasId = DefId; + type ImplOrTraitAssocTyId = DefId; + type ImplOrTraitAssocConstId = DefId; + type ImplOrTraitAssocTermId = DefId; + type InherentAssocTyId = DefId; + type InherentAssocConstId = DefId; + type InherentAssocTermId = DefId; type Span = Span; type GenericArgs = ty::GenericArgsRef<'tcx>; @@ -288,7 +302,15 @@ fn mk_type_list_from_iter(self, args: I) -> T::Output self.mk_type_list_from_iter(args) } - fn parent(self, def_id: DefId) -> DefId { + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { + self.parent(def_id) + } + + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTyId) -> DefId { + self.parent(def_id) + } + + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId { self.parent(def_id) } @@ -446,7 +468,7 @@ fn has_target_features(self, def_id: DefId) -> bool { !self.codegen_fn_attrs(def_id).target_features.is_empty() } - fn require_lang_item(self, lang_item: SolverLangItem) -> DefId { + fn require_projection_lang_item(self, lang_item: SolverProjectionLangItem) -> DefId { self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP) } @@ -458,7 +480,7 @@ fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> DefId { self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP) } - fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { + fn is_projection_lang_item(self, def_id: DefId, lang_item: SolverProjectionLangItem) -> bool { self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) } @@ -478,7 +500,7 @@ fn is_sizedness_trait(self, def_id: DefId) -> bool { self.is_sizedness_trait(def_id) } - fn as_lang_item(self, def_id: DefId) -> Option { + fn as_projection_lang_item(self, def_id: DefId) -> Option { lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?) } @@ -757,7 +779,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { } bidirectional_lang_item_map! { - SolverLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; + SolverProjectionLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; // tidy-alphabetical-start AsyncFnKindUpvars, @@ -766,7 +788,6 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { CallRefFuture, CoroutineReturn, CoroutineYield, - DynMetadata, FieldBase, FieldType, FutureOutput, @@ -778,6 +799,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { SolverAdtLangItem, fn lang_item_to_solver_adt_lang_item, fn solver_adt_lang_item_to_lang_item; // tidy-alphabetical-start + DynMetadata, Option, Poll, // tidy-alphabetical-end @@ -791,7 +813,6 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { AsyncFnKindHelper, AsyncFnMut, AsyncFnOnce, - AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, Clone, diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 729a5049d633..1f22e0af9c00 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -7,11 +7,10 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{ - HashingControls, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + HashingControls, StableHash, StableHashCtxt, StableHasher, }; use tracing::trace; -use crate::middle::region; use crate::{mir, ty}; impl<'tcx, H, T> StableHash for &'tcx ty::list::RawList @@ -45,20 +44,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl<'tcx, H, T> ToStableHashKey for &'tcx ty::list::RawList -where - T: StableHash, -{ - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Fingerprint { - let mut hasher = StableHasher::new(); - self.stable_hash(hcx, &mut hasher); - hasher.finish() - } -} - impl<'tcx> StableHash for ty::GenericArg<'tcx> { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { self.kind().stable_hash(hcx, hasher); @@ -81,12 +66,3 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas self.into_parts().stable_hash(hcx, hasher); } } - -impl ToStableHashKey for region::Scope { - type KeyType = region::Scope; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> region::Scope { - *self - } -} diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index b95b565322f1..ff7e518f91a8 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -590,6 +590,12 @@ fn expr_as_place( let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) } + ExprKind::Reborrow { .. } => { + // FIXME(reborrow): it should currently be impossible to end up evaluating a + // Reborrow expression as a place. That might not in the future, but what this then + // evaluates to requires further thought. + unreachable!(); + } } } 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 3cf4e43160e5..903ff07a8893 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -434,6 +434,10 @@ pub(crate) fn as_rvalue( ); block.and(Rvalue::Use(operand, WithRetag::Yes)) } + ExprKind::Reborrow { source, mutability, target } => { + let temp = unpack!(block = this.as_temp(block, scope, source, mutability)); + block.and(Rvalue::Reborrow(target, mutability, temp.into())) + } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 5404d9800c3f..eb6a0754358d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -43,7 +43,8 @@ pub(crate) fn of(ek: &ExprKind<'_>) -> Option { | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } | ExprKind::PlaceUnwrapUnsafeBinder { .. } - | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::Reborrow { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 08379760d8c1..7413ab9bd238 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -908,6 +908,16 @@ fn is_supported_loop_match_type(ty: Ty<'_>) -> bool { this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } + ExprKind::Reborrow { source, mutability, target } => { + let place = unpack!(block = this.as_place(block, source)); + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Reborrow(target, mutability, place.into()), + ); + block.unit() + } }; if !expr_is_block_or_scope { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index aceddcc54de9..055932b5c30d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -397,7 +397,8 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { | ExprKind::If { .. } | ExprKind::InlineAsm { .. } | ExprKind::LogicalOp { .. } - | ExprKind::Use { .. } => { + | ExprKind::Use { .. } + | ExprKind::Reborrow { .. } => { // We don't need to save the old value and restore it // because all the place expressions can't have more // than one child. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 69260792a95d..b32d7dce4f4d 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -221,6 +221,13 @@ fn apply_adjustment( debug!(?kind); kind } + Adjust::GenericReborrow(mutability) => { + let expr = self.thir.exprs.push(expr); + let kind = + ExprKind::Reborrow { source: expr, mutability, target: adjustment.target }; + + kind + } }; Expr { temp_scope_id, ty: adjustment.target, span, kind } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 4dc3e02ace71..5b786f5a710f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -364,6 +364,12 @@ fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool { | VarRef { .. } | ZstLiteral { .. } | Yield { .. } => true, + ExprKind::Reborrow { .. } => { + // FIXME(reborrow): matching on a Reborrow expression should be impossible + // currently. Whether this remains to be true, and if the reborrow result then is a + // known valid scrutinee requires further thought. + unreachable!("Reborrow expression in match") + } } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index ea34e5f4d97d..5330e3397db8 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -600,6 +600,13 @@ fn print_expr_kind(&mut self, expr_kind: &ExprKind<'tcx>, depth_lvl: usize) { self.print_expr(*value, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } + ExprKind::Reborrow { source: _, mutability: _, target: _ } => { + print_indented!(self, "Reborrow {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + print_indented!(self, "mutability:", depth_lvl + 1); + print_indented!(self, "ty:", depth_lvl + 1); + print_indented!(self, "}", depth_lvl); + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index d5548266aa01..9ec68f5260c0 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -78,7 +78,8 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { // We ignore fake borrows as these get removed after analysis and shouldn't effect // the layout of generators. Rvalue::RawPtr(_, borrowed_place) - | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => { + | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) + | Rvalue::Reborrow(_, _, borrowed_place) => { if !borrowed_place.is_indirect() { self.trans.gen_(borrowed_place.local); } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ab53baae4327..905f36a3edb0 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -438,7 +438,10 @@ fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { } } Rvalue::CopyForDeref(..) => unreachable!(), - Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) => {} + Rvalue::Ref(..) + | Rvalue::Reborrow(..) + | Rvalue::RawPtr(..) + | Rvalue::Discriminant(..) => {} } } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index a6a60fddf909..fc31d502087f 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -23,6 +23,9 @@ fn visit_assign( rvalue: &mut Rvalue<'tcx>, location: Location, ) { + if rvalue.is_generic_reborrow() { + return; + } // We don't need to do anything for deref temps as they are // not part of the source code, but used for desugaring purposes. if self.local_decls[place.local].is_deref_temp() { diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index c28147ff7eb1..1c465977ec6c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -469,7 +469,7 @@ fn handle_rvalue( Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand, _) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), - Rvalue::Ref(..) | Rvalue::RawPtr(..) => { + Rvalue::Ref(..) | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) => { // We don't track such places. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a7376fa7e65a..77f068ab7a66 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1073,6 +1073,21 @@ fn simplify_rvalue( self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Ref(borrow_kind)); } + Rvalue::Reborrow(_, mutbl, place) => { + if mutbl == Mutability::Mut { + // Note: this is adapted from simplify_aggregate. + let mut operand = Operand::Copy(place); + let val = self.simplify_operand(&mut operand, location); + // FIXME(reborrow): Is it correct to make these retagging assignments? + *rvalue = Rvalue::Use(Operand::Copy(place), WithRetag::Yes); + return val; + } else { + // FIXME(reborrow): CoerceShared should perform effectively a copy followed by a + // transmute, or possibly something more complicated in the future. For now we + // leave this unoptimised. + return None; + } + } Rvalue::RawPtr(mutbl, ref mut place) => { self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Address(mutbl)); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 45c8bae295d9..6c2ca9166b10 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -421,8 +421,8 @@ fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option< } // Do not try creating references (#67862) - Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) => { - trace!("skipping RawPtr | Ref for {:?}", place); + Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(_, _, place) => { + trace!("skipping RawPtr | Ref | Reborrow for {:?}", place); // This may be creating mutable references or immutable references to cells. // If that happens, the pointed to value could be mutated via that reference. @@ -553,7 +553,7 @@ fn eval_rvalue(&mut self, rvalue: &Rvalue<'tcx>, dest: &Place<'tcx>) -> Option<( self.eval_operand(operand)?.into() } - CopyForDeref(place) => self.eval_place(place)?.into(), + CopyForDeref(place) | Reborrow(_, _, place) => self.eval_place(place)?.into(), BinaryOp(bin_op, box (ref left, ref right)) => { let left = self.eval_operand(left)?; diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index e450e6754da1..e0b6b2a1104e 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -96,6 +96,7 @@ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | Rvalue::UnaryOp(..) | Rvalue::BinaryOp(..) | Rvalue::Ref(..) + | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) => false, }; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 041ff45c11d7..340975c8fa70 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -580,6 +580,12 @@ fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> self.validate_ref(*kind, place)?; } + Rvalue::Reborrow(_, _, place) => { + // FIXME(reborrow): should probably have a place_simplified like above. + let op = &Operand::Copy(*place); + self.validate_operand(op)? + } + Rvalue::Aggregate(_, operands) => { for o in operands { self.validate_operand(o)?; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index a6cbd93a6a73..746662e6a302 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1152,7 +1152,7 @@ macro_rules! check_kinds { ); } } - Rvalue::Ref(..) => {} + Rvalue::Ref(..) | Rvalue::Reborrow(..) => {} Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 429206a93dfe..8fd7d6d0471c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -69,7 +69,7 @@ fn instantiate_canonical_var( fn add_item_bounds_for_hidden_type( &self, - def_id: ::DefId, + def_id: ::OpaqueTyId, args: ::GenericArgs, param_env: ::ParamEnv, hidden_ty: ::Ty, @@ -79,7 +79,7 @@ fn add_item_bounds_for_hidden_type( fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, - trait_assoc_def_id: ::DefId, + trait_assoc_def_id: ::TraitAssocTermId, impl_def_id: ::ImplId, ) -> FetchEligibleAssocItemResponse; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 0bbc6f483104..0053e25e6365 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -4,7 +4,7 @@ use derive_where::derive_where; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ @@ -106,7 +106,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait( // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args).skip_norm_wip()])) + Ok(ty::Binder::dummy(vec![ + cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(), + ])) } } } @@ -541,7 +543,8 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( let nested = vec![ bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), ]; - let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); + let future_output_def_id = + cx.require_projection_lang_item(SolverProjectionLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -642,7 +646,8 @@ fn coroutine_closure_to_ambiguous_coroutine( args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { - let upvars_projection_def_id = cx.require_lang_item(SolverLangItem::AsyncFnKindUpvars); + let upvars_projection_def_id = + cx.require_projection_lang_item(SolverProjectionLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( cx, upvars_projection_def_id, @@ -920,7 +925,10 @@ pub(in crate::solve) fn predicates_for_object_candidate( // show up in the bounds, but just ones that come from substituting // `Self` with the dyn type. let proj = proj.with_self_ty(cx, trait_ref.self_ty()); - replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj)); + replace_projection_with + .entry(proj.def_id().into()) + .or_default() + .push(bound.rebind(proj)); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index e4d3e881f6af..a03d3182b9fe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -702,7 +702,7 @@ fn should_rerun_after_erased_canonicalization( original_typing_mode: TypingMode, parent_opaque_types: &[(OpaqueTypeKey, I::Ty)], ) -> RerunDecision { - let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id); + let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id.into()); let opaque_in_storage = |opaques: I::LocalDefIds, defids: SmallCopyList<_>| { if defids.as_ref().is_empty() { RerunDecision::No @@ -1357,7 +1357,7 @@ pub(super) fn trait_ref_is_knowable( pub(super) fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, - trait_assoc_def_id: I::DefId, + trait_assoc_def_id: I::TraitAssocTermId, impl_def_id: I::ImplId, ) -> FetchEligibleAssocItemResponse { self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) @@ -1374,7 +1374,7 @@ pub(super) fn register_hidden_type_in_storage( pub(super) fn add_item_bounds_for_hidden_type( &mut self, - opaque_def_id: I::DefId, + opaque_def_id: I::OpaqueTyId, opaque_args: I::GenericArgs, param_env: I::ParamEnv, hidden_ty: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 178a192a6630..5dc3162fbd2a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -357,7 +357,7 @@ fn structurally_normalize_term( } } - fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { + fn opaque_type_is_rigid(&self, def_id: I::OpaqueTyId) -> bool { match self .typing_mode() // Caller should handle erased mode @@ -370,7 +370,7 @@ fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques } | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { - !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) + !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id.into())) } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 72e8d1be5915..44b8929c45f4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -13,6 +13,7 @@ impl EvalCtxt<'_, D> pub(super) fn normalize_anon_const( &mut self, goal: Goal>, + def_id: I::UnevaluatedConstId, ) -> QueryResult { let uv = goal.predicate.alias.expect_ct(self.cx()); self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index 44fe2913b73d..f70f4bede33d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -32,11 +32,13 @@ pub(super) fn normalize_free_alias( let actual = match free_alias.kind(cx) { ty::AliasTermKind::FreeTy { def_id } => { - cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() - } - ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => { - cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + cx.type_of(def_id.into()).instantiate(cx, free_alias.args).skip_norm_wip().into() } + ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id.into()) => cx + .const_of_item(def_id.into()) + .instantiate(cx, free_alias.args) + .skip_norm_wip() + .into(), ty::AliasTermKind::FreeConst { .. } => { return self.evaluate_const_and_instantiate_normalizes_to_term( goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 00b5fd7fdcbc..d9eeb3e90f95 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -18,18 +18,19 @@ impl EvalCtxt<'_, D> pub(super) fn normalize_inherent_associated_term( &mut self, goal: Goal>, + def_id: I::InherentAssocTermId, ) -> QueryResult { let cx = self.cx(); let inherent = goal.predicate.alias; - let impl_def_id = cx.parent(inherent.def_id()); - let impl_args = self.fresh_args_for_item(impl_def_id); + let impl_def_id = cx.inherent_alias_term_parent(def_id); + let impl_args = self.fresh_args_for_item(impl_def_id.into()); // Equate impl header and add impl where clauses self.eq( goal.param_env, inherent.self_ty(), - cx.type_of(impl_def_id).instantiate(cx, impl_args).skip_norm_wip(), + cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), )?; // Equate IAT with the RHS of the project goal @@ -46,7 +47,7 @@ pub(super) fn normalize_inherent_associated_term( // to be very careful when changing the impl where-clauses to be productive. self.add_goals( GoalSource::Misc, - cx.predicates_of(inherent.def_id()) + cx.predicates_of(def_id.into()) .iter_instantiated(cx, inherent_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), @@ -54,11 +55,13 @@ pub(super) fn normalize_inherent_associated_term( let normalized = match inherent.kind(cx) { ty::AliasTermKind::InherentTy { def_id } => { - cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() - } - ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => { - cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + cx.type_of(def_id.into()).instantiate(cx, inherent_args).skip_norm_wip().into() } + ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => cx + .const_of_item(def_id.into()) + .instantiate(cx, inherent_args) + .skip_norm_wip() + .into(), ty::AliasTermKind::InherentConst { .. } => { // FIXME(gca): This is dead code at the moment. It should eventually call // self.evaluate_const like projected consts do in consider_impl_candidate in diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 3b2e13fbb22d..ce9663666ed9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -5,7 +5,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason}; use rustc_type_ir::{ self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, @@ -32,74 +32,79 @@ pub(super) fn compute_normalizes_to_goal( goal: Goal>, ) -> QueryResult { debug_assert!(self.term_is_fully_unconstrained(goal)); - let cx = self.cx(); - match goal.predicate.alias.kind(cx) { + match goal.predicate.alias.kind { ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { - let trait_ref = goal.predicate.alias.trait_ref(cx); - let (_, proven_via) = - self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - let trait_goal: Goal> = goal.with(cx, trait_ref); - ecx.compute_trait_goal(trait_goal) - })?; - self.assemble_and_merge_candidates( - proven_via, - goal, - |ecx| { - // FIXME(generic_associated_types): Addresses aggressive inference in #92917. - // - // If this type is a GAT with currently unconstrained arguments, we do not - // want to normalize it via a candidate which only applies for a specific - // instantiation. We could otherwise keep the GAT as rigid and succeed this way. - // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. - // - // This only avoids normalization if a GAT argument is fully unconstrained. - // This is quite arbitrary but fixing it causes some ambiguity, see #125196. - for arg in goal.predicate.alias.own_args(cx).iter() { - let Some(term) = arg.as_term() else { - continue; - }; - match ecx.structurally_normalize_term(goal.param_env, term) { - Ok(term) => { - if term.is_infer() { - return Some( - ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ), - ); - } - } - Err(NoSolution) => return Some(Err(NoSolution)), - } - } - - None - }, - |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }) - .enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - }) - .map_err(Into::into) - }, - ) + self.normalize_associated_term(goal) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { - self.normalize_inherent_associated_term(goal) + ty::AliasTermKind::InherentTy { def_id } => { + self.normalize_inherent_associated_term(goal, def_id.into()) } - ty::AliasTermKind::OpaqueTy { .. } => self.normalize_opaque_type(goal), + ty::AliasTermKind::InherentConst { def_id } => { + self.normalize_inherent_associated_term(goal, def_id.into()) + } + ty::AliasTermKind::OpaqueTy { def_id } => self.normalize_opaque_type(goal, def_id), ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { self.normalize_free_alias(goal) } - ty::AliasTermKind::UnevaluatedConst { .. } => self.normalize_anon_const(goal), + ty::AliasTermKind::UnevaluatedConst { def_id } => { + self.normalize_anon_const(goal, def_id) + } } } + fn normalize_associated_term(&mut self, goal: Goal>) -> QueryResult { + let cx = self.cx(); + + let trait_ref = goal.predicate.alias.trait_ref(cx); + let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { + let trait_goal: Goal> = goal.with(cx, trait_ref); + ecx.compute_trait_goal(trait_goal) + })?; + self.assemble_and_merge_candidates( + proven_via, + goal, + |ecx| { + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if a GAT argument is fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + match ecx.structurally_normalize_term(goal.param_env, term) { + Ok(term) => { + if term.is_infer() { + return Some(ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + )); + } + } + Err(NoSolution) => return Some(Err(NoSolution)), + } + } + + None + }, + |ecx| { + ecx.probe(|&result| ProbeKind::RigidAlias { result }) + .enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + .map_err(Into::into) + }, + ) + } + /// When normalizing an associated item, constrain the expected term to `term`. /// /// We know `term` to always be a fully unconstrained inference variable, so @@ -282,7 +287,7 @@ fn consider_impl_candidate( let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, - goal.predicate.def_id(), + goal.predicate.def_id().try_into().unwrap(), impl_def_id, ) { FetchEligibleAssocItemResponse::Found(target_item_def_id) => target_item_def_id, @@ -354,7 +359,7 @@ fn consider_impl_candidate( } } - let target_container_def_id = cx.parent(target_item_def_id); + let target_container_def_id = cx.impl_or_trait_assoc_term_parent(target_item_def_id); // Getting the right args here is complex, e.g. given: // - a goal ` as Trait>::Assoc` @@ -371,10 +376,10 @@ fn consider_impl_candidate( impl_def_id, impl_args, impl_trait_ref, - target_container_def_id, + target_container_def_id.into(), )?; - if !cx.check_args_compatible(target_item_def_id, target_args) { + if !cx.check_args_compatible(target_item_def_id.into(), target_args) { return error_response( ecx, cx.delay_bug("associated item has mismatched arguments"), @@ -384,21 +389,21 @@ fn consider_impl_candidate( // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy { .. } => cx - .type_of(target_item_def_id) + .type_of(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() .into(), ty::AliasTermKind::ProjectionConst { .. } - if cx.is_type_const(target_item_def_id) => + if cx.is_type_const(target_item_def_id.into()) => { - cx.const_of_item(target_item_def_id) + cx.const_of_item(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() .into() } ty::AliasTermKind::ProjectionConst { .. } => { let uv = ty::UnevaluatedConst::new( - target_item_def_id.try_into().unwrap(), + target_item_def_id.into().try_into().unwrap(), target_args, ); return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv); @@ -504,6 +509,7 @@ fn consider_builtin_async_fn_trait_candidates( goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { let cx = ecx.cx(); + let def_id = goal.predicate.def_id().try_into().unwrap(); let env_region = match goal_kind { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2), @@ -531,41 +537,42 @@ fn consider_builtin_async_fn_trait_candidates( [output_coroutine_ty], ); - let (projection_term, term) = - if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - output_coroutine_ty.into(), - ) - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - env_region.into(), - ], - ), - output_coroutine_ty.into(), - ) - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - coroutine_return_ty.into(), - ) - } else { - panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) - }; + let (projection_term, term) = if cx + .is_projection_lang_item(def_id, SolverProjectionLangItem::CallOnceFuture) + { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CallRefFuture) { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::AsyncFnOnceOutput) { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + coroutine_return_ty.into(), + ) + } else { + panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) + }; let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx); Self::probe_and_consider_implied_clause( @@ -639,8 +646,8 @@ fn consider_builtin_pointee_candidate( goal: Goal, ) -> Result, NoSolution> { let cx = ecx.cx(); - let metadata_def_id = cx.require_lang_item(SolverLangItem::Metadata); - assert_eq!(metadata_def_id, goal.predicate.def_id()); + let metadata_def_id = cx.require_projection_lang_item(SolverProjectionLangItem::Metadata); + assert_eq!(Into::::into(metadata_def_id), goal.predicate.def_id()); let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -666,8 +673,8 @@ fn consider_builtin_pointee_candidate( ty::Str | ty::Slice(_) => Ty::new_usize(cx), ty::Dynamic(_, _) => { - let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); - cx.type_of(dyn_metadata) + let dyn_metadata = cx.require_adt_lang_item(SolverAdtLangItem::DynMetadata); + cx.type_of(dyn_metadata.into()) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) .skip_norm_wip() } @@ -865,10 +872,12 @@ fn consider_builtin_coroutine_candidate( } let coroutine = args.as_coroutine(); + let def_id = goal.predicate.def_id().try_into().unwrap(); - let term = if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineReturn) { + let term = if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineReturn) + { coroutine.return_ty().into() - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineYield) { + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineYield) { coroutine.yield_ty().into() } else { panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id()) @@ -992,9 +1001,10 @@ fn consider_builtin_field_candidate( else { return Err(NoSolution); }; - let ty = match ecx.cx().as_lang_item(goal.predicate.def_id()) { - Some(SolverLangItem::FieldBase) => base, - Some(SolverLangItem::FieldType) => ty, + let ty = match ecx.cx().as_projection_lang_item(goal.predicate.def_id().try_into().unwrap()) + { + Some(SolverProjectionLangItem::FieldBase) => base, + Some(SolverProjectionLangItem::FieldType) => ty, _ => panic!("unexpected associated type {:?} in `Field`", goal.predicate), }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 45dd2f25abd7..f25ac21f4a62 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -17,6 +17,7 @@ impl EvalCtxt<'_, D> pub(super) fn normalize_opaque_type( &mut self, goal: Goal>, + def_id: I::OpaqueTyId, ) -> QueryResult { let cx = self.cx(); let opaque_ty = goal.predicate.alias; @@ -27,7 +28,7 @@ pub(super) fn normalize_opaque_type( // An impossible opaque type bound is the only way this goal will fail // e.g. assigning `impl Copy := NotCopy` self.add_item_bounds_for_hidden_type( - opaque_ty.def_id(), + def_id, opaque_ty.args, goal.param_env, expected, @@ -43,10 +44,9 @@ pub(super) fn normalize_opaque_type( defining_opaque_types_and_generators: defining_opaque_types, } | TypingMode::Borrowck { defining_opaque_types } => { - let Some(def_id) = opaque_ty - .def_id() + let Some(def_id) = def_id .as_local() - .filter(|&def_id| defining_opaque_types.contains(&def_id)) + .filter(|&def_id| defining_opaque_types.contains(&def_id.into())) else { // If we're not in the defining scope, treat the alias as rigid. self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); @@ -134,7 +134,7 @@ pub(super) fn normalize_opaque_type( TypingMode::PostAnalysis => { // FIXME: Add an assertion that opaque type storage is empty. let actual = - cx.type_of(opaque_ty.def_id()).instantiate(cx, opaque_ty.args).skip_norm_wip(); + cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args).skip_norm_wip(); self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 4d5fe3bf6950..a4f35f763e8e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -242,7 +242,7 @@ fn consider_auto_trait_candidate( } debug_assert!(ecx.opaque_type_is_rigid(def_id)); - for item_bound in cx.item_self_bounds(def_id).skip_binder() { + for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() { if item_bound .as_trait_clause() .is_some_and(|b| b.def_id() == goal.predicate.def_id()) diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 6aeed20b1f48..f9b5f9af951e 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -586,6 +586,13 @@ pub enum Rvalue { /// Yields the operand unchanged, except for possibly a retag. Use(Operand, WithRetag), + + /// Creates a bitwise copy of the source type, producing either a value of the same type (when + /// Mutability::Mut) or a different type with a guaranteed equal memory layout defined by the + /// CoerceShared trait. See [`Rvalue::Reborrow`] for a more detailed explanation. + /// + /// [`Rvalue::Reborrow`]: rustc_middle::mir::Rvalue::Reborrow + Reborrow(Ty, Mutability, Place), } impl Rvalue { @@ -600,6 +607,7 @@ pub fn ty(&self, locals: &[LocalDecl]) -> Result { let place_ty = place.ty(locals)?; Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) } + Rvalue::Reborrow(target, _, _) => Ok(*target), Rvalue::AddressOf(mutability, place) => { let place_ty = place.ty(locals)?; Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index dac39c636e31..dec4044fd260 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -379,6 +379,13 @@ fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { }; write!(writer, "{kind}{place:?}") } + Rvalue::Reborrow(target, mutability, place) => { + let kind = match mutability { + Mutability::Not => "Reborrow", + Mutability::Mut => "CoerceShared", + }; + write!(writer, "${kind}({place:?}) as {target}") + } Rvalue::Repeat(op, cnst) => { write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) } diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index cf6540cc6c77..5a3afc993735 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -272,6 +272,11 @@ fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; self.visit_place(place, pcx, location); } + Rvalue::Reborrow(target, mutability, place) => { + self.visit_ty(target, location); + let pcx = PlaceContext { is_mut: matches!(mutability, Mutability::Mut) }; + self.visit_place(place, pcx, location); + } Rvalue::Repeat(op, constant) => { self.visit_operand(op, location); self.visit_ty_const(constant, location); diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 43c81e3dc02a..7e76d5a91ac6 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -204,6 +204,11 @@ fn stable<'cx>( kind.stable(tables, cx), place.stable(tables, cx), ), + Reborrow(target, kind, place) => crate::mir::Rvalue::Reborrow( + target.stable(tables, cx), + kind.stable(tables, cx), + place.stable(tables, cx), + ), ThreadLocalRef(def_id) => { crate::mir::Rvalue::ThreadLocalRef(tables.crate_item(*def_id)) } diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index ed9ad8c7a0a6..b614bc14b453 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -294,7 +294,7 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( // re-executing the query since `try_start` only checks that the query is not currently // executing, but another thread may have already completed the query and stores it result // in the query cache. - if tcx.sess.threads() > 1 { + if tcx.sess.threads().is_some() { if let Some((value, index)) = query.cache.lookup(&key) { tcx.prof.query_cache_hit(index.into()); return (value, Some(index)); diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3b58ebaf8cc7..c37838706301 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -721,13 +721,15 @@ fn build_reduced_graph_for_use_tree( decls: Default::default(), nested, id, + def_id: feed.def_id(), }; self.add_import(module_path, kind, use_tree.span(), item, root_span, item.id, vis); } ast::UseTreeKind::Glob(_) => { if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { - let kind = ImportKind::Glob { max_vis: CmCell::new(None), id }; + let kind = + ImportKind::Glob { max_vis: CmCell::new(None), id, def_id: feed.def_id() }; self.add_import(prefix, kind, use_tree.span(), item, root_span, item.id, vis); } else { // Resolve the prelude import early. @@ -1019,7 +1021,12 @@ fn build_reduced_graph_for_extern_crate( }) .unwrap_or((true, None, self.r.dummy_decl)); let import = self.r.arenas.alloc_import(ImportData { - kind: ImportKind::ExternCrate { source: orig_name, target: orig_ident, id: item.id }, + kind: ImportKind::ExternCrate { + source: orig_name, + target: orig_ident, + id: item.id, + def_id: local_def_id, + }, root_id: item.id, parent_scope, imported_module: CmCell::new(module), @@ -1271,7 +1278,7 @@ fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: Nod if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); let nrules = self.r.local_macro_map[&def_id].nrules; - self.r.unused_macro_rules.insert(node_id, DenseBitSet::new_filled(nrules)); + self.r.unused_macro_rules.insert(node_id, (def_id, DenseBitSet::new_filled(nrules))); } } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 7e1b1bce3ff7..8ab0087ae615 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -431,8 +431,7 @@ pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { } } } - ImportKind::ExternCrate { id, .. } => { - let def_id = self.local_def_id(id); + ImportKind::ExternCrate { id, def_id, .. } => { if self.extern_crate_map.get(&def_id).is_none_or(|&cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index b5614106fe99..693e49995c1c 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -45,10 +45,7 @@ fn nearest_normal_mod(&self, def_id: LocalDefId) -> LocalDefId { fn private_vis_import(&self, decl: Decl<'_>) -> Visibility { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; Visibility::Restricted( - import - .id() - .map(|id| self.nearest_normal_mod(self.local_def_id(id))) - .unwrap_or(CRATE_DEF_ID), + import.def_id().map(|id| self.nearest_normal_mod(id)).unwrap_or(CRATE_DEF_ID), ) } @@ -96,8 +93,8 @@ pub(crate) fn compute_effective_visibilities<'c>( // is the maximum value among visibilities of declarations corresponding to that def id. for (decl, eff_vis) in visitor.import_effective_visibilities.iter() { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; - if let Some(node_id) = import.id() { - r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + if let Some(def_id) = import.def_id() { + r.effective_visibilities.update_eff_vis(def_id, eff_vis, r.tcx) } if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) { exported_ambiguities.insert(*decl); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index e63ab9f21a6a..1b9e5d2a2daa 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -16,7 +16,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_hir::def::{self, DefKind, PartialRes}; -use rustc_hir::def_id::{DefId, LocalDefIdMap}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::{TyCtxt, Visibility}; @@ -90,17 +90,20 @@ pub(crate) enum ImportKind<'ra> { /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree` /// for `a` in this field. id: NodeId, + def_id: LocalDefId, }, Glob { // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. max_vis: CmCell>, id: NodeId, + def_id: LocalDefId, }, ExternCrate { source: Option, target: Ident, id: NodeId, + def_id: LocalDefId, }, MacroUse { /// A field has been added indicating whether it should be reported as a lint, @@ -116,7 +119,7 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use ImportKind::*; match self { - Single { source, target, decls, nested, id, .. } => f + Single { source, target, decls, nested, id, def_id } => f .debug_struct("Single") .field("source", source) .field("target", target) @@ -127,15 +130,20 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ) .field("nested", nested) .field("id", id) + .field("def_id", def_id) .finish(), - Glob { max_vis, id } => { - f.debug_struct("Glob").field("max_vis", max_vis).field("id", id).finish() - } - ExternCrate { source, target, id } => f + Glob { max_vis, id, def_id } => f + .debug_struct("Glob") + .field("max_vis", max_vis) + .field("id", id) + .field("def_id", def_id) + .finish(), + ExternCrate { source, target, id, def_id } => f .debug_struct("ExternCrate") .field("source", source) .field("target", target) .field("id", id) + .field("def_id", def_id) .finish(), MacroUse { warn_private } => { f.debug_struct("MacroUse").field("warn_private", warn_private).finish() @@ -260,12 +268,20 @@ pub(crate) fn id(&self) -> Option { } } - pub(crate) fn simplify(&self, r: &Resolver<'_, '_>) -> Reexport { - let to_def_id = |id| r.local_def_id(id).to_def_id(); + pub(crate) fn def_id(&self) -> Option { match self.kind { - ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)), - ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)), - ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)), + ImportKind::Single { def_id, .. } + | ImportKind::Glob { def_id, .. } + | ImportKind::ExternCrate { def_id, .. } => Some(def_id), + ImportKind::MacroUse { .. } | ImportKind::MacroExport => None, + } + } + + pub(crate) fn simplify(&self) -> Reexport { + match self.kind { + ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), + ImportKind::Glob { def_id, .. } => Reexport::Glob(def_id.to_def_id()), + ImportKind::ExternCrate { def_id, .. } => Reexport::ExternCrate(def_id.to_def_id()), ImportKind::MacroUse { .. } => Reexport::MacroUse, ImportKind::MacroExport => Reexport::MacroExport, } @@ -340,13 +356,16 @@ struct UnresolvedImportError { // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` // are permitted for backward-compatibility under a deprecation lint. -fn pub_use_of_private_extern_crate_hack(import: ImportSummary, decl: Decl<'_>) -> Option { +fn pub_use_of_private_extern_crate_hack( + import: ImportSummary, + decl: Decl<'_>, +) -> Option { match (import.is_single, decl.kind) { (true, DeclKind::Import { import: decl_import, .. }) - if let ImportKind::ExternCrate { id, .. } = decl_import.kind + if let ImportKind::ExternCrate { def_id, .. } = decl_import.kind && import.vis.is_public() => { - Some(id) + Some(def_id) } _ => None, } @@ -845,8 +864,7 @@ pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet) -> Option (source, target, decls, id), - ImportKind::Glob { ref max_vis, id } => { + ImportKind::Glob { ref max_vis, id, def_id } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1294,7 +1312,6 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option) -> bool { // This function is only called for single imports. - let ImportKind::Single { source, target, ref decls, id, .. } = import.kind else { + let ImportKind::Single { source, target, ref decls, id, def_id, .. } = import.kind else { unreachable!() }; @@ -1675,7 +1692,7 @@ pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'ra>) -> boo // Skip if the import is public or was used through non scope-based resolution, // e.g. through a module-relative path. if self.import_use_map.get(&import) == Some(&Used::Other) - || self.effective_visibilities.is_exported(self.local_def_id(id)) + || self.effective_visibilities.is_exported(def_id) { return false; } @@ -1829,23 +1846,23 @@ fn finalize_resolutions_in( let mut children = Vec::new(); let mut ambig_children = Vec::new(); - module.to_module().for_each_child(self, |this, ident, orig_ident_span, _, binding| { + module.to_module().for_each_child(self, |_this, ident, orig_ident_span, _, binding| { let res = binding.res().expect_non_local(); if res != def::Res::Err { let ident = ident.orig(orig_ident_span); let child = |reexport_chain| ModChild { ident, res, vis: binding.vis(), reexport_chain }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { - let main = child(ambig_binding1.reexport_chain(this)); + let main = child(ambig_binding1.reexport_chain()); let second = ModChild { ident, res: ambig_binding2.res().expect_non_local(), vis: ambig_binding2.vis(), - reexport_chain: ambig_binding2.reexport_chain(this), + reexport_chain: ambig_binding2.reexport_chain(), }; ambig_children.push(AmbigModChild { main, second }) } else { - children.push(child(binding.reexport_chain(this))); + children.push(child(binding.reexport_chain())); } } }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 91511cbcb65a..4352e1046290 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1171,11 +1171,11 @@ fn macro_kinds(&self) -> Option { self.res().macro_kinds() } - fn reexport_chain(self: Decl<'ra>, r: &Resolver<'_, '_>) -> SmallVec<[Reexport; 2]> { + fn reexport_chain(self: Decl<'ra>) -> SmallVec<[Reexport; 2]> { let mut reexport_chain = SmallVec::new(); let mut next_binding = self; while let DeclKind::Import { source_decl, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify(r)); + reexport_chain.push(import.simplify()); next_binding = source_decl; } reexport_chain @@ -1405,8 +1405,8 @@ pub struct Resolver<'ra, 'tcx> { local_macro_def_scopes: FxHashMap> = default::fx_hash_map(), ast_transform_scopes: FxHashMap> = default::fx_hash_map(), unused_macros: FxIndexMap, - /// A map from the macro to all its potentially unused arms. - unused_macro_rules: FxIndexMap>, + /// A map from the macro to all its potentially unused arms and the `LocalDefId` of the macro itself. + unused_macro_rules: FxIndexMap)>, proc_macro_stubs: FxHashSet = default::fx_hash_set(), /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -2154,8 +2154,7 @@ fn find_transitive_imports( ) -> &'tcx [LocalDefId] { let mut import_ids: SmallVec<[LocalDefId; 1]> = smallvec![]; while let DeclKind::Import { import, source_decl, .. } = kind { - if let Some(node_id) = import.id() { - let def_id = self.local_def_id(node_id); + if let Some(def_id) = import.def_id() { self.maybe_unused_trait_imports.insert(def_id); import_ids.push(def_id); } @@ -2291,8 +2290,7 @@ fn record_use_inner( #[inline] fn add_to_glob_map(&mut self, import: Import<'_>, name: Symbol) { - if let ImportKind::Glob { id, .. } = import.kind { - let def_id = self.local_def_id(id); + if let ImportKind::Glob { def_id, .. } = import.kind { self.glob_map.entry(def_id).or_default().insert(name); } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index de818d933c9b..d20907f53f66 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -339,7 +339,7 @@ fn resolve_macro_invocation( } fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { - if let Some(rules) = self.unused_macro_rules.get_mut(&id) { + if let Some((_, rules)) = self.unused_macro_rules.get_mut(&id) { rules.remove(rule_i); } } @@ -356,11 +356,10 @@ fn check_unused_macros(&mut self) { self.unused_macro_rules.swap_remove(&node_id); } - for (&node_id, unused_arms) in self.unused_macro_rules.iter() { + for (&node_id, (def_id, unused_arms)) in self.unused_macro_rules.iter() { if unused_arms.is_empty() { continue; } - let def_id = self.local_def_id(node_id); let m = &self.local_macro_map[&def_id]; let SyntaxExtensionKind::MacroRules(ref m) = m.ext.kind else { continue; diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index d66e04f58106..79d05e2d20a6 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start getopts = "0.2" -rand = "0.9.0" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f8da18632a99..9d0de38b3a39 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -14,7 +14,7 @@ use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtFlags}; use rustc_feature::UnstableFeatures; @@ -628,7 +628,6 @@ pub enum OutputType { )* } - impl StableOrd for OutputType { const CAN_USE_UNSTABLE_SORT: bool = true; @@ -636,15 +635,6 @@ impl StableOrd for OutputType { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } - impl ToStableHashKey for OutputType { - type KeyType = Self; - - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - *self - } - } - - impl OutputType { pub fn iter_all() -> impl Iterator { static ALL_VARIANTS: &[OutputType] = &[ @@ -1107,13 +1097,10 @@ pub fn file_for_writing( outputs: &OutputFilenames, flavor: OutputType, codegen_unit_name: &str, - invocation_temp: Option<&str>, ) -> PathBuf { match *self { OutFileName::Real(ref path) => path.clone(), - OutFileName::Stdout => { - outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp) - } + OutFileName::Stdout => outputs.temp_path_for_cgu(flavor, codegen_unit_name), } } @@ -1138,6 +1125,17 @@ pub struct OutputFilenames { filestem: String, pub single_output_file: Option, temps_directory: Option, + + /// A random string generated per invocation of rustc. + /// + /// This is prepended to all temporary files so that they do not collide + /// during concurrent invocations of rustc, or past invocations that were + /// preserved with a flag like `-C save-temps`, since these files may be + /// hard linked. + // This does not affect incr comp outputs, only where temp files are stored. + #[stable_hasher(ignore)] + invocation_temp: Option, + explicit_dwo_out_directory: Option, pub outputs: OutputTypes, } @@ -1180,6 +1178,7 @@ pub fn new( out_filestem: String, single_output_file: Option, temps_directory: Option, + invocation_temp: Option, explicit_dwo_out_directory: Option, extra: String, outputs: OutputTypes, @@ -1188,6 +1187,7 @@ pub fn new( out_directory, single_output_file, temps_directory, + invocation_temp, explicit_dwo_out_directory, outputs, crate_stem: format!("{out_crate_name}{extra}"), @@ -1224,23 +1224,14 @@ fn output_path(&self, flavor: OutputType) -> PathBuf { /// Gets the path where a compilation artifact of the given type for the /// given codegen unit should be placed on disk. If codegen_unit_name is /// None, a path distinct from those of any codegen unit will be generated. - pub fn temp_path_for_cgu( - &self, - flavor: OutputType, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { + pub fn temp_path_for_cgu(&self, flavor: OutputType, codegen_unit_name: &str) -> PathBuf { let extension = flavor.extension(); - self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp) + self.temp_path_ext_for_cgu(extension, codegen_unit_name) } /// Like `temp_path`, but specifically for dwarf objects. - pub fn temp_path_dwo_for_cgu( - &self, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { - let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp); + pub fn temp_path_dwo_for_cgu(&self, codegen_unit_name: &str) -> PathBuf { + let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name); if let Some(dwo_out) = &self.explicit_dwo_out_directory { let mut o = dwo_out.clone(); o.push(p.file_name().unwrap()); @@ -1252,16 +1243,11 @@ pub fn temp_path_dwo_for_cgu( /// Like `temp_path`, but also supports things where there is no corresponding /// OutputType, like noopt-bitcode or lto-bitcode. - pub fn temp_path_ext_for_cgu( - &self, - ext: &str, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { + pub fn temp_path_ext_for_cgu(&self, ext: &str, codegen_unit_name: &str) -> PathBuf { let mut extension = codegen_unit_name.to_string(); // Append `.{invocation_temp}` to ensure temporary files are unique. - if let Some(rng) = invocation_temp { + if let Some(rng) = &self.invocation_temp { extension.push('.'); extension.push_str(rng); } @@ -1302,10 +1288,9 @@ pub fn split_dwarf_path( split_debuginfo_kind: SplitDebuginfo, split_dwarf_kind: SplitDwarfKind, cgu_name: &str, - invocation_temp: Option<&str>, ) -> Option { - let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp); - let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp); + let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name); + let dwo_out = self.temp_path_dwo_for_cgu(cgu_name); match (split_debuginfo_kind, split_dwarf_kind) { (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes @@ -2494,11 +2479,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M cg.codegen_units, ); - if unstable_opts.threads == 0 { - early_dcx.early_fatal("value for threads must be a positive non-zero integer"); - } - - if unstable_opts.threads == parse::MAX_THREADS_CAP { + if unstable_opts.threads == Some(parse::MAX_THREADS_CAP) { early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP)); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4767b0cfbe86..51b2635a4188 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -759,7 +759,7 @@ mod desc { pub(crate) const parse_number: &str = "a number"; pub(crate) const parse_opt_number: &str = parse_number; pub(crate) const parse_frame_pointer: &str = "one of `true`/`yes`/`on`, `false`/`no`/`off`, or (with -Zunstable-options) `non-leaf` or `always`"; - pub(crate) const parse_threads: &str = parse_number; + pub(crate) const parse_threads: &str = "a number or `sync`"; pub(crate) const parse_time_passes_format: &str = "`text` (default) or `json`"; pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; @@ -1067,22 +1067,23 @@ pub(crate) fn parse_opt_comma_list(slot: &mut Option>, v: Option<&st } } - pub(crate) fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { - let ret = match v.and_then(|s| s.parse().ok()) { - Some(0) => { - *slot = std::thread::available_parallelism().map_or(1, NonZero::::get); - true - } - Some(i) => { - *slot = i; - true - } - None => false, + pub(crate) fn parse_threads(slot: &mut Option, v: Option<&str>) -> bool { + let Some(s) = v else { return false }; + if s == "sync" { + // Enable synchronization despite only using one thread. + *slot = Some(1); + return true; + } + let n = match s.parse().ok() { + Some(0) => std::thread::available_parallelism().map_or(1, NonZero::::get), + Some(i) => i, + None => return false, }; // We want to cap the number of threads here to avoid large numbers like 999999 and compiler panics. // This solution was suggested here https://github.com/rust-lang/rust/issues/117638#issuecomment-1800925067 - *slot = slot.clone().min(MAX_THREADS_CAP); - ret + let n = n.min(MAX_THREADS_CAP); + *slot = (n > 1).then_some(n); // Enable synchronization if we're using more than one thread. + true } /// Use this for any numeric option that has a static default. @@ -2672,12 +2673,12 @@ pub(crate) fn parse_assert_incr_state( #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), - /// We default to 1 here since we want to behave like + /// We default to None here since we want to behave like /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get /// the num_cpus behavior. #[rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field")] - threads: usize = (1, parse_threads, [UNTRACKED], + threads: Option = (None, parse_threads, [UNTRACKED], "use a thread pool with N threads"), time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3b2fc53381a9..f67feeab6573 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -5,8 +5,6 @@ use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::{env, io}; -use rand::{RngCore, rng}; -use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; @@ -163,14 +161,6 @@ pub struct Session { target_filesearch: FileSearch, host_filesearch: FileSearch, - /// A random string generated per invocation of rustc. - /// - /// This is prepended to all temporary files so that they do not collide - /// during concurrent invocations of rustc, or past invocations that were - /// preserved with a flag like `-C save-temps`, since these files may be - /// hard linked. - pub invocation_temp: Option, - /// The names of intrinsics that the current codegen backend replaces /// with its own implementations. pub replaced_intrinsics: FxHashSet, @@ -813,10 +803,12 @@ pub fn must_emit_unwind_tables(&self) -> bool { .unwrap_or(self.panic_strategy().unwinds() || self.target.default_uwtable) } - /// Returns the number of query threads that should be used for this - /// compilation + /// Returns the number of threads used for the thread pool. + /// + /// `None` means thread pool is not used and synchronization is disabled. + /// `Some(n)` means synchronization is enabled with `n` worker threads. #[inline] - pub fn threads(&self) -> usize { + pub fn threads(&self) -> Option { self.opts.unstable_opts.threads } @@ -1097,11 +1089,6 @@ pub fn build_session( filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); - let invocation_temp = sopts - .incremental - .as_ref() - .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); - let timings = TimingSectionHandler::new(sopts.json_timings); let sess = Session { @@ -1132,7 +1119,6 @@ pub fn build_session( file_depinfo: Default::default(), target_filesearch, host_filesearch, - invocation_temp, replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` thin_lto_supported: true, // filled by `run_compiler` mir_opt_bisect_eval_count: AtomicUsize::new(0), diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index d07c7fcfd468..6ce3d6b309f0 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -465,24 +465,6 @@ fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash } } -impl ToStableHashKey for CrateNum { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.as_def_id().to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for DefPathHash { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> DefPathHash { - *self - } -} - macro_rules! typed_def_id { ($Name:ident, $LocalName:ident) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, StableHash)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ea2880d8d7df..90f6bf669a2b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -8,7 +8,7 @@ use rustc_arena::DroplessArena; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::stable_hasher::{ - StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + StableCompare, StableHash, StableHashCtxt, StableHasher, }; use rustc_data_structures::sync::Lock; use rustc_macros::{Decodable, Encodable, StableHash, symbols}; @@ -286,6 +286,7 @@ Rc, RcWeak, Ready, + Reborrow, RefCell, Reference, Relaxed, @@ -624,6 +625,7 @@ cmse_nonsecure_entry, coerce_pointee_validated, coerce_shared, + coerce_shared_target, coerce_unsized, coff, cold, @@ -1929,6 +1931,7 @@ sinf128, size, size_of, + size_of_type_id, size_of_val, sized, sized_hierarchy, @@ -2641,14 +2644,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl ToStableHashKey for Symbol { - type KeyType = String; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> String { - self.as_str().to_string() - } -} - impl StableCompare for Symbol { const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 1f0f564a7790..587eeac14beb 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -110,6 +110,14 @@ macro_rules! args { // representation, so this is disabled. generate_arange_section: false, + // Differ from LLVM's default to use the legacy exception-handling + // proposal instructions and use the standard exception-handling + // instructions. Note that this is only applicable when unwinding is + // actually turned on, which it's not by default on this target. For + // `-Zbuild-std` builds, however, this affects when rebuilding libstd + // with unwinding. + llvm_args: cvs!["-wasm-use-legacy-eh=false"], + ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 4b6f5b655760..e640e3116459 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -31,6 +31,11 @@ pub(crate) fn target() -> Target { panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], + // Explicitly override the `base::wasm`'s `llvm_args` back to empty. The + // base is to force using the most standard exception-handling + // instructions, when enabled, but this target is intended to follow + // Emscripten, which is whatever LLVM defaults to. + llvm_args: cvs![], ..base::wasm::options() }; Target { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 49e0bdde3787..8ed32df65645 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -208,6 +208,9 @@ fn recurse_build<'tcx>( | ExprKind::ThreadLocalRef(_) => { error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? } + ExprKind::Reborrow { .. } => { + todo!(); + } }) } @@ -305,6 +308,9 @@ fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { | thir::ExprKind::InlineAsm(_) | thir::ExprKind::ThreadLocalRef(_) | thir::ExprKind::Yield { .. } => false, + thir::ExprKind::Reborrow { .. } => { + todo!(); + } } } fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index b55cbc3164d9..de3e04626f82 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -375,13 +375,17 @@ pub struct EarlyBinder { impl Eq for EarlyBinder {} -/// For early binders, you should first call `instantiate` before using any visitors. +// FIXME(154045): Recommended as per https://github.com/rust-lang/rust/issues/154045, this is so sad :(( #[cfg(feature = "nightly")] -impl !TypeFoldable for ty::EarlyBinder {} +macro_rules! generate { ($( $tt:tt )*) => { $( $tt )* } } -/// For early binders, you should first call `instantiate` before using any visitors. #[cfg(feature = "nightly")] -impl !TypeVisitable for ty::EarlyBinder {} +generate!( + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeFoldable for ty::EarlyBinder {} + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeVisitable for ty::EarlyBinder {} +); impl EarlyBinder { pub fn bind(value: T) -> EarlyBinder { diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 4e4be901dbff..26d98b5d8ad7 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -5,12 +5,6 @@ use rustc_ast_ir::Mutability; #[cfg(feature = "nightly")] -use rustc_data_structures::fingerprint::Fingerprint; -#[cfg(feature = "nightly")] -use rustc_data_structures::stable_hasher::{ - StableHash, StableHashCtxt, StableHasher, ToStableHashKey, -}; -#[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}; use crate::inherent::*; @@ -48,18 +42,6 @@ pub enum SimplifiedType { Error, } -#[cfg(feature = "nightly")] -impl ToStableHashKey for SimplifiedType { - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Fingerprint { - let mut hasher = StableHasher::new(); - self.stable_hash(hcx, &mut hasher); - hasher.finish() - } -} - /// Generic parameters are pretty much just bound variables, e.g. /// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as /// `for<'a, T> fn(&'a T) -> u32`. diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 50c30f425270..19c59df0604c 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -118,6 +118,11 @@ pub struct TypeFlags: u32 { /// Does this have any `ReErased` regions? const HAS_RE_ERASED = 1 << 21; + /// Does this have any regions of any kind? + const HAS_REGIONS = TypeFlags::HAS_FREE_REGIONS.bits() + | TypeFlags::HAS_RE_BOUND.bits() + | TypeFlags::HAS_RE_ERASED.bits(); + /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? const STILL_FURTHER_SPECIALIZABLE = TypeFlags::HAS_TY_PARAM.bits() @@ -139,7 +144,7 @@ pub struct TypeFlags: u32 { /// Does this type have any coroutines in it? const HAS_TY_CORO = 1 << 25; - /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`? + /// Does this have a `Bound(BoundVarIndexKind::Canonical, _)`? const HAS_CANONICAL_BOUND = 1 << 26; } } @@ -192,7 +197,7 @@ pub fn for_clauses(clauses: &[I::Clause]) -> FlagComputation { } fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | flags; + self.flags |= flags; } /// indicates that `self` refers to something at binding level `binder` diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d1a50599e8b9..0fe68b525669 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -55,7 +55,7 @@ use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, BoundVarIndexKind, Interner, TypeFlags}; +use crate::{self as ty, BoundVarIndexKind, Interner}; /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. @@ -121,10 +121,6 @@ fn try_super_fold_with>( /// default that does an "identity" fold. Implementations of these methods /// often fall back to a `super_fold_with` method if the primary argument /// doesn't satisfy a particular condition. -/// -/// A blanket implementation of [`FallibleTypeFolder`] will defer to -/// the infallible methods of this trait to ensure that the two APIs -/// are coherent. pub trait TypeFolder: Sized { fn cx(&self) -> I; @@ -437,6 +433,10 @@ fn fold_const(&mut self, ct: I::Const) -> I::Const { fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c } + } } pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Region { @@ -477,10 +477,10 @@ pub fn fold_regions( /// Folds over the substructure of a type, visiting its component /// types and all regions that occur *free* within it. /// -/// That is, function pointer types and trait object can introduce -/// new bound regions which are not visited by this visitors as +/// That is, function pointer types and trait objects can introduce +/// new bound regions which are not visited by this visitor as /// they are not free; only regions that occur free will be -/// visited by `fld_r`. +/// visited by `fold_region_fn`. pub struct RegionFolder { cx: I, @@ -489,7 +489,7 @@ pub struct RegionFolder { /// binder, it is incremented (via `shift_in`). current_index: ty::DebruijnIndex, - /// Callback invokes for each free region. The `DebruijnIndex` + /// Callback invoked for each free region. The `DebruijnIndex` /// points to the binder *just outside* the ones we have passed /// through. fold_region_fn: F, @@ -539,32 +539,18 @@ fn fold_region(&mut self, r: I::Region) -> I::Region { } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if t.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - t.super_fold_with(self) - } else { - t - } + if t.has_regions() { t.super_fold_with(self) } else { t } } fn fold_const(&mut self, ct: I::Const) -> I::Const { - if ct.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - ct.super_fold_with(self) - } else { - ct - } + if ct.has_regions() { ct.super_fold_with(self) } else { ct } } fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { - if p.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - p.super_fold_with(self) - } else { - p - } + if p.has_regions() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_regions() { c.super_fold_with(self) } else { c } } } diff --git a/compiler/rustc_type_ir/src/generic_visit.rs b/compiler/rustc_type_ir/src/generic_visit.rs index 6669168443a4..6adaf4d158f4 100644 --- a/compiler/rustc_type_ir/src/generic_visit.rs +++ b/compiler/rustc_type_ir/src/generic_visit.rs @@ -212,6 +212,7 @@ fn generic_visit_with(&self, _visitor: &mut V) {} rustc_hash::FxBuildHasher, crate::TypeFlags, crate::solve::GoalSource, + crate::solve::VisibleForLeakCheck, rustc_abi::ExternAbi, ); diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f63361f5968d..7ff447a81a28 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -55,7 +55,11 @@ pub trait Ty>: fn new_alias(interner: I, alias_ty: ty::AliasTy) -> Self; - fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self { + fn new_projection_from_args( + interner: I, + def_id: I::TraitAssocTyId, + args: I::GenericArgs, + ) -> Self { Self::new_alias( interner, ty::AliasTy::new_from_args(interner, ty::AliasTyKind::Projection { def_id }, args), @@ -64,7 +68,7 @@ fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) fn new_projection( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTyId, args: impl IntoIterator>, ) -> Self { Self::new_alias( @@ -637,19 +641,24 @@ pub trait Features: Copy { } #[rust_analyzer::prefer_underscore_import] -pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { +pub trait DefId::LocalDefId>: + Copy + Debug + Hash + Eq + TypeFoldable +{ fn is_local(self) -> bool; - fn as_local(self) -> Option; + fn as_local(self) -> Option; } -pub trait SpecificDefId: - DefId + Into + TryFrom +pub trait SpecificDefId::LocalDefId>: + DefId + Into + TryFrom { } -impl + Into + TryFrom> - SpecificDefId for T +impl< + I: Interner, + T: DefId + Into + TryFrom, + Local, +> SpecificDefId for T { } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e13c4279a68a..f25bc5fc4e3c 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -9,7 +9,7 @@ use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; -use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use crate::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use crate::relate::Relate; use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, @@ -56,6 +56,38 @@ fn next_trait_solver_globally(self) -> bool { type AdtId: SpecificDefId; type ImplId: SpecificDefId; type UnevaluatedConstId: SpecificDefId; + type TraitAssocTyId: SpecificDefId + + Into + + TryFrom; + type TraitAssocConstId: SpecificDefId + + Into + + Into + + TryFrom; + type TraitAssocTermId: SpecificDefId; + type OpaqueTyId: SpecificDefId; + type LocalOpaqueTyId: Copy + + Debug + + Hash + + Eq + + Into + + Into + + Into + + TypeFoldable; + type FreeTyAliasId: SpecificDefId + Into; + type FreeConstAliasId: SpecificDefId + + Into + + Into; + type FreeTermAliasId: SpecificDefId; + type ImplOrTraitAssocTyId: SpecificDefId + Into; + type ImplOrTraitAssocConstId: SpecificDefId + + Into + + Into; + type ImplOrTraitAssocTermId: SpecificDefId; + type InherentAssocTyId: SpecificDefId + Into; + type InherentAssocConstId: SpecificDefId + + Into + + Into; + type InherentAssocTermId: SpecificDefId; type Span: Span; type GenericArgs: GenericArgs; @@ -203,8 +235,10 @@ fn opt_alias_variances( ) -> Option; fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; - fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) - -> ty::EarlyBinder; + fn type_of_opaque_hir_typeck( + self, + def_id: Self::LocalOpaqueTyId, + ) -> ty::EarlyBinder; fn is_type_const(self, def_id: Self::DefId) -> bool; fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder; fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; @@ -219,7 +253,7 @@ fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) fn trait_ref_and_own_args_for_alias( self, - def_id: Self::DefId, + def_id: Self::TraitAssocTermId, args: Self::GenericArgs, ) -> (ty::TraitRef, Self::GenericArgsSlice); @@ -243,7 +277,12 @@ fn mk_type_list_from_iter(self, args: I) -> T::Output I: Iterator, T: CollectAndApply; - fn parent(self, def_id: Self::DefId) -> Self::DefId; + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId; + + /// This can be an impl, or a trait if this is a defaulted term. + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTermId) -> Self::DefId; + + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId; fn recursion_limit(self) -> usize; @@ -325,13 +364,20 @@ fn explicit_implied_const_bounds( fn has_target_features(self, def_id: Self::FunctionId) -> bool; - fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId; + fn require_projection_lang_item( + self, + lang_item: SolverProjectionLangItem, + ) -> Self::TraitAssocTyId; fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> Self::TraitId; fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> Self::AdtId; - fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool; + fn is_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + lang_item: SolverProjectionLangItem, + ) -> bool; fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool; @@ -341,7 +387,10 @@ fn explicit_implied_const_bounds( fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool; - fn as_lang_item(self, def_id: Self::DefId) -> Option; + fn as_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + ) -> Option; fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option; @@ -360,7 +409,7 @@ fn for_each_relevant_impl( ); fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId)); - fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn has_item_definition(self, def_id: Self::ImplOrTraitAssocTermId) -> bool; fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool; diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 5af2bd811bab..c6ab804d81c9 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,11 +1,12 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, Binder, ClosureKind, CoercePredicate, ExistentialProjection, - ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, - PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, - UnevaluatedConst, + AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, + ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; +#[cfg(feature = "nightly")] +use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index f1c45a4d98b5..4ed574a55ece 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -1,6 +1,6 @@ /// Lang items used by the new trait solver. This can be mapped to whatever internal /// representation of `LangItem`s used in the underlying compiler implementation. -pub enum SolverLangItem { +pub enum SolverProjectionLangItem { // tidy-alphabetical-start AsyncFnKindUpvars, AsyncFnOnceOutput, @@ -8,7 +8,6 @@ pub enum SolverLangItem { CallRefFuture, CoroutineReturn, CoroutineYield, - DynMetadata, FieldBase, FieldType, FutureOutput, @@ -18,6 +17,7 @@ pub enum SolverLangItem { pub enum SolverAdtLangItem { // tidy-alphabetical-start + DynMetadata, Option, Poll, // tidy-alphabetical-end @@ -29,7 +29,6 @@ pub enum SolverTraitLangItem { AsyncFnKindHelper, AsyncFnMut, AsyncFnOnce, - AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, Clone, diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 782a7d30b675..d050cc2fe05a 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -13,7 +13,7 @@ derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] pub struct OpaqueTypeKey { - pub def_id: I::LocalDefId, + pub def_id: I::LocalOpaqueTyId, pub args: I::GenericArgs, } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 7b815e61cf09..301cf7dbf108 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -472,7 +472,7 @@ pub fn with_self_ty(&self, cx: I, self_ty: I::Ty) -> ty::Binder> derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] pub struct ExistentialProjection { - pub def_id: I::DefId, + pub def_id: I::TraitAssocTermId, pub args: I::GenericArgs, pub term: I::Term, @@ -487,17 +487,17 @@ impl Eq for ExistentialProjection {} impl ExistentialProjection { pub fn new_from_args( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTermId, args: I::GenericArgs, term: I::Term, ) -> ExistentialProjection { - interner.debug_assert_existential_args_compatible(def_id, args); + interner.debug_assert_existential_args_compatible(def_id.into(), args); Self { def_id, args, term, use_existential_projection_new_instead: () } } pub fn new( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTermId, args: impl IntoIterator>, term: I::Term, ) -> ExistentialProjection { @@ -511,10 +511,10 @@ pub fn new( /// then this function would return an `exists T. T: Iterator` existential trait /// reference. pub fn trait_ref(&self, interner: I) -> ExistentialTraitRef { - let def_id = interner.parent(self.def_id); - let args_count = interner.generics_of(def_id).count() - 1; + let def_id = interner.projection_parent(self.def_id); + let args_count = interner.generics_of(def_id.into()).count() - 1; let args = interner.mk_args(&self.args.as_slice()[..args_count]); - ExistentialTraitRef::new_from_args(interner, def_id.try_into().unwrap(), args) + ExistentialTraitRef::new_from_args(interner, def_id, args) } pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate { @@ -524,7 +524,7 @@ pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate) projection_predicate.projection_term.args.type_at(0); Self { - def_id: projection_predicate.projection_term.def_id(), + def_id: projection_predicate.def_id(), args: interner.mk_args(&projection_predicate.projection_term.args.as_slice()[1..]), term: projection_predicate.term, use_existential_projection_new_instead: (), @@ -549,13 +549,13 @@ pub fn with_self_ty(&self, cx: I, self_ty: I::Ty) -> ty::Binder I::DefId { + pub fn item_def_id(&self) -> I::TraitAssocTermId { self.skip_binder().def_id } } #[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] -#[derive(Lift_Generic)] +#[derive(Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) @@ -570,12 +570,12 @@ pub enum AliasTermKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - ProjectionTy { def_id: I::DefId }, + ProjectionTy { def_id: I::TraitAssocTyId }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - InherentTy { def_id: I::DefId }, + InherentTy { def_id: I::InherentAssocTyId }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -585,22 +585,22 @@ pub enum AliasTermKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - OpaqueTy { def_id: I::DefId }, + OpaqueTy { def_id: I::OpaqueTyId }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - FreeTy { def_id: I::DefId }, + FreeTy { def_id: I::FreeTyAliasId }, /// An unevaluated anonymous constants. - UnevaluatedConst { def_id: I::DefId }, + UnevaluatedConst { def_id: I::UnevaluatedConstId }, /// An unevaluated const coming from an associated const. - ProjectionConst { def_id: I::DefId }, + ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. - FreeConst { def_id: I::DefId }, + FreeConst { def_id: I::FreeConstAliasId }, /// An associated const in an inherent `impl` - InherentConst { def_id: I::DefId }, + InherentConst { def_id: I::InherentAssocConstId }, } impl AliasTermKind { @@ -631,17 +631,18 @@ pub fn is_type(self) -> bool { } } - // FIXME: replace with explicit matches + // FIXME(#156181): replace with explicit matches pub fn def_id(self) -> I::DefId { - let (AliasTermKind::ProjectionTy { def_id } - | AliasTermKind::InherentTy { def_id } - | AliasTermKind::OpaqueTy { def_id } - | AliasTermKind::FreeTy { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } - | AliasTermKind::FreeConst { def_id } - | AliasTermKind::InherentConst { def_id }) = self; - def_id + match self { + AliasTermKind::ProjectionTy { def_id } => def_id.into(), + AliasTermKind::InherentTy { def_id } => def_id.into(), + AliasTermKind::OpaqueTy { def_id } => def_id.into(), + AliasTermKind::FreeTy { def_id } => def_id.into(), + AliasTermKind::UnevaluatedConst { def_id } => def_id.into(), + AliasTermKind::ProjectionConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::InherentConst { def_id } => def_id.into(), + } } } @@ -737,11 +738,11 @@ pub fn expect_ty(self, interner: I) -> ty::AliasTy { } pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { - let def_id = match self.kind(interner) { - AliasTermKind::InherentConst { def_id } - | AliasTermKind::FreeConst { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } => def_id, + let def = match self.kind(interner) { + AliasTermKind::InherentConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::UnevaluatedConst { def_id } => def_id, + AliasTermKind::ProjectionConst { def_id } => def_id.into(), kind @ (AliasTermKind::ProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } @@ -749,7 +750,7 @@ pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) } }; - ty::UnevaluatedConst { def: def_id.try_into().unwrap(), args: self.args } + ty::UnevaluatedConst { def, args: self.args } } // FIXME: remove this function (access the field instead) @@ -763,17 +764,14 @@ pub fn def_id(self) -> I::DefId { } pub fn to_term(self, interner: I) -> I::Term { + let unevaluated_const = |def_id| { + I::Const::new_unevaluated(interner, ty::UnevaluatedConst::new(def_id, self.args)).into() + }; let alias_ty_kind = match self.kind(interner) { - AliasTermKind::FreeConst { def_id } - | AliasTermKind::InherentConst { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } => { - return I::Const::new_unevaluated( - interner, - ty::UnevaluatedConst::new(def_id.try_into().unwrap(), self.args), - ) - .into(); - } + AliasTermKind::FreeConst { def_id } => return unevaluated_const(def_id.into()), + AliasTermKind::InherentConst { def_id } => return unevaluated_const(def_id.into()), + AliasTermKind::UnevaluatedConst { def_id } => return unevaluated_const(def_id), + AliasTermKind::ProjectionConst { def_id } => return unevaluated_const(def_id.into()), AliasTermKind::ProjectionTy { def_id } => ty::Projection { def_id }, AliasTermKind::InherentTy { def_id } => ty::Inherent { def_id }, @@ -804,15 +802,25 @@ pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { ) } + fn projection_def_id(self) -> Option { + match self.kind { + AliasTermKind::ProjectionTy { def_id } => Some(def_id.into()), + AliasTermKind::ProjectionConst { def_id } => Some(def_id.into()), + AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } + | AliasTermKind::UnevaluatedConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::InherentConst { .. } => None, + } + } + + fn expect_projection_def_id(self) -> I::TraitAssocTermId { + self.projection_def_id().expect("expected a projection") + } + pub fn trait_def_id(self, interner: I) -> I::TraitId { - assert!( - matches!( - self.kind(interner), - AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } - ), - "expected a projection" - ); - interner.parent(self.def_id()).try_into().unwrap() + interner.projection_parent(self.expect_projection_def_id()) } /// Extracts the underlying trait reference and own args from this projection. @@ -820,7 +828,7 @@ pub fn trait_def_id(self, interner: I) -> I::TraitId { /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::GenericArgsSlice) { - interner.trait_ref_and_own_args_for_alias(self.def_id(), self.args) + interner.trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args) } /// Extracts the underlying trait reference from this projection. @@ -918,8 +926,8 @@ pub fn trait_def_id(self, interner: I) -> I::TraitId { self.projection_term.trait_def_id(interner) } - pub fn def_id(self) -> I::DefId { - self.projection_term.def_id() + pub fn def_id(self) -> I::TraitAssocTermId { + self.projection_term.expect_projection_def_id() } } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 425436dabafb..51de99e92fca 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -268,7 +268,10 @@ fn relate>( b: ty::ExistentialProjection, ) -> RelateResult> { if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.def_id, b.def_id))) + Err(TypeError::ProjectionMismatched(ExpectedFound::new( + a.def_id.into(), + b.def_id.into(), + ))) } else { let term = relation.relate_with_variance( ty::Invariant, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 0634c9b741d3..cf9530378dc7 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -535,7 +535,7 @@ pub enum BuiltinImplSource { #[derive_where(Copy, Clone, Debug; I: Interner)] pub enum FetchEligibleAssocItemResponse { Err(I::ErrorGuaranteed), - Found(I::DefId), + Found(I::ImplOrTraitAssocTermId), NotFound(TypingMode), NotFoundBecauseErased, } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index a08bd00eeed6..0fc39660015d 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -39,12 +39,12 @@ pub enum AliasTyKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - Projection { def_id: I::DefId }, + Projection { def_id: I::TraitAssocTyId }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - Inherent { def_id: I::DefId }, + Inherent { def_id: I::InherentAssocTyId }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -55,13 +55,13 @@ pub enum AliasTyKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - Opaque { def_id: I::DefId }, + Opaque { def_id: I::OpaqueTyId }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - Free { def_id: I::DefId }, + Free { def_id: I::FreeTyAliasId }, } impl AliasTyKind { @@ -79,12 +79,12 @@ pub fn descr(self) -> &'static str { } pub fn def_id(self) -> I::DefId { - let (AliasTyKind::Projection { def_id } - | AliasTyKind::Inherent { def_id } - | AliasTyKind::Opaque { def_id } - | AliasTyKind::Free { def_id }) = self; - - def_id + match self { + AliasTyKind::Projection { def_id } => def_id.into(), + AliasTyKind::Inherent { def_id } => def_id.into(), + AliasTyKind::Opaque { def_id } => def_id.into(), + AliasTyKind::Free { def_id } => def_id.into(), + } } } @@ -506,10 +506,10 @@ pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { ) } - pub fn trait_def_id(self, interner: I) -> I::DefId { + pub fn trait_def_id(self, interner: I) -> I::TraitId { let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - interner.parent(def_id) + interner.projection_parent(def_id.into()) } /// Extracts the underlying trait reference and own args from this projection. @@ -520,7 +520,7 @@ pub fn trait_def_id(self, interner: I) -> I::DefId { pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - interner.trait_ref_and_own_args_for_alias(def_id, self.args) + interner.trait_ref_and_own_args_for_alias(def_id.into(), self.args) } /// Extracts the underlying trait reference from this projection. diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index a078b860be77..492c37481298 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -67,7 +67,7 @@ pub trait TypeVisitable: fmt::Debug { /// each field/element. /// /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as + /// calls a visitor method specifically for that type (such as /// `V::visit_ty`). This is where control transfers from `TypeVisitable` to /// `TypeVisitor`. fn visit_with>(&self, visitor: &mut V) -> V::Result; @@ -102,8 +102,8 @@ fn visit_ty(&mut self, t: I::Ty) -> Self::Result { t.super_visit_with(self) } - // The default region visitor is a no-op because `Region` is non-recursive - // and has no `super_visit_with` method to call. + // `Region` is non-recursive so the default region visitor has no + // `super_visit_with` method to call. fn visit_region(&mut self, r: I::Region) -> Self::Result { if let ty::ReError(guar) = r.kind() { self.visit_error(guar) @@ -251,10 +251,10 @@ fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { self.has_vars_bound_at_or_above(binder.shifted_in(1)) } - /// Return `true` if this type has regions that are not a part of the type. - /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)` - /// would return `true`. The latter can occur when traversing through the - /// former. + /// Returns `true` if this type has regions that are not a part of the + /// type. For example, given a `for<'a> fn(&'a i32)` this function returns + /// `false`, while given a `fn(&'a i32)` it returns `true`. The latter can + /// occur when traversing through the former. /// /// See [`HasEscapingVarsVisitor`] for more information. fn has_escaping_bound_vars(&self) -> bool { @@ -285,6 +285,10 @@ fn has_non_region_param(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) } + fn has_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_REGIONS) + } + fn has_infer_regions(&self) -> bool { self.has_type_flags(TypeFlags::HAS_RE_INFER) } @@ -363,13 +367,12 @@ fn has_non_region_error(&self) -> bool { impl> TypeVisitableExt for T { fn has_type_flags(&self, flags: TypeFlags) -> bool { - let res = - self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags); - res + self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags) } fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }) + == ControlFlow::Break(FoundEscapingVars) } fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> { @@ -438,8 +441,7 @@ fn visit_binder>(&mut self, t: &ty::Binder) -> Self::R #[inline] fn visit_ty(&mut self, t: I::Ty) -> Self::Result { // Note: no `super_visit_with` call. - let flags = t.flags(); - if flags.intersects(self.flags) { + if t.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) @@ -449,8 +451,7 @@ fn visit_ty(&mut self, t: I::Ty) -> Self::Result { #[inline] fn visit_region(&mut self, r: I::Region) -> Self::Result { // Note: no `super_visit_with` call, as usual for `Region`. - let flags = r.flags(); - if flags.intersects(self.flags) { + if r.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) @@ -571,7 +572,7 @@ fn visit_const(&mut self, ct: I::Const) -> Self::Result { // `outer_index`, that means that `ct` contains some content // bound at `outer_index` or above (because // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. + // content in `ct`). Therefore, `ct` has some escaping vars. if ct.outer_exclusive_binder() > self.outer_index { ControlFlow::Break(FoundEscapingVars) } else { diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7d820403ccb7..62300d8b70a9 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -839,7 +839,7 @@ pub const fn trait_info_of_trait_type_id( self, trait_represented_by_type_id: TypeId, ) -> Option> { - if self.info().size.is_none() { + if self.size().is_none() { return None; } diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 17792dca583d..b2ff54bdfa21 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -1,8 +1,8 @@ use crate::marker::{Destruct, PhantomData}; -use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst}; -use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut}; +use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst, transmute}; +use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, without_provenance_mut}; -impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { +impl<'l, 'f, T, U, F: FnMut(T) -> U> Drain<'l, 'f, T, F> { /// This function returns a function that lets you index the given array in const. /// As implemented it can optimize better than iterators, and can be constified. /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented @@ -14,9 +14,11 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { /// This will also not actually store the array. /// /// SAFETY: must only be called `N` times. Thou shalt not drop the array either. - // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] - pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self { + pub(super) const unsafe fn new( + array: &'l mut ManuallyDrop<[T; N]>, + f: &'f mut F, + ) -> Self { // dont drop the array, transfers "ownership" to Self let ptr: NonNull = NonNull::from_mut(array).cast(); // SAFETY: @@ -24,16 +26,17 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { // at the end of `slice`. `end` will never be dereferenced, only checked // for direct pointer equality with `ptr` to check if the drainer is done. unsafe { - let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; - Self { ptr, end, f, l: PhantomData } + let end_or_len = + if T::IS_ZST { without_provenance_mut(N) } else { ptr.as_ptr().add(N) }; + Self { ptr, end_or_len, f, l: PhantomData } } } } /// See [`Drain::new`]; this is our fake iterator. #[unstable(feature = "array_try_map", issue = "79711")] -pub(super) struct Drain<'l, 'f, T, const N: usize, F> { - // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. +pub(super) struct Drain<'l, 'f, T, F> { + // FIXME(const-hack): This is a slice::IterMut<'l>, replace when possible. /// The pointer to the next element to return, or the past-the-end location /// if the drainer is empty. /// @@ -41,16 +44,16 @@ pub(super) struct Drain<'l, 'f, T, const N: usize, F> { /// As we "own" this array, we dont need to store any lifetime. ptr: NonNull, /// For non-ZSTs, the non-null pointer to the past-the-end element. - /// For ZSTs, this is null. - end: *mut T, + /// For ZSTs, this is the number of unprocessed items. + end_or_len: *mut T, f: &'f mut F, - l: PhantomData<&'l mut [T; N]>, + l: PhantomData<&'l mut [T]>, } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> +impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, F> where F: [const] FnMut(T) -> U, { @@ -63,7 +66,7 @@ extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> +impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, F> where F: [const] FnMut(T) -> U, { @@ -73,6 +76,16 @@ extern "rust-call" fn call_mut( (_ /* ignore argument */,): (usize,), ) -> Self::Output { if T::IS_ZST { + #[expect(ptr_to_integer_transmute_in_consts)] + // SAFETY: + // This is equivalent to `self.end_or_len.addr`, but that's not + // available in `const`. `self.end_or_len` doesn't have provenance, + // so transmuting is fine. + let len = unsafe { transmute::<*mut T, usize>(self.end_or_len) }; + // SAFETY: + // The caller guarantees that this is never called more than N times + // (see `Drain::new`), hence this cannot underflow. + self.end_or_len = without_provenance_mut(unsafe { len.unchecked_sub(1) }); // its UB to call this more than N times, so returning more ZSTs is valid. // SAFETY: its a ZST? we conjur. (self.f)(unsafe { conjure_zst::() }) @@ -88,20 +101,32 @@ extern "rust-call" fn call_mut( } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const Drop for Drain<'_, '_, T, N, F> { +impl const Drop for Drain<'_, '_, T, F> { fn drop(&mut self) { - if !T::IS_ZST { + let slice = if T::IS_ZST { + from_raw_parts_mut::<[T]>( + self.ptr.as_ptr(), + #[expect(ptr_to_integer_transmute_in_consts)] + // SAFETY: + // This is equivalent to `self.end_or_len.addr`, but that's not + // available in `const`. `self.end_or_len` doesn't have provenance, + // so transmuting is fine. + unsafe { + transmute::<*mut T, usize>(self.end_or_len) + }, + ) + } else { // SAFETY: we cant read more than N elements - let slice = unsafe { + unsafe { from_raw_parts_mut::<[T]>( self.ptr.as_ptr(), // SAFETY: `start <= end` - self.end.offset_from_unsigned(self.ptr.as_ptr()), + self.end_or_len.offset_from_unsigned(self.ptr.as_ptr()), ) - }; + } + }; - // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) - unsafe { drop_in_place(slice) } - } + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) + unsafe { drop_in_place(slice) } } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c71085ef9f97..8eaf91dab8df 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2937,6 +2937,15 @@ pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { a.data == b.data } +/// Gets the size of the type represented by this `TypeId`. +/// +/// The stabilized version of this intrinsic is [`core::any::TypeId::size`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn size_of_type_id(_id: crate::any::TypeId) -> Option { + panic!("`Type::size` can only be called at compile-time") +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index f56a4d7308e9..9e6b02f00983 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1347,3 +1347,19 @@ pub trait FnPtr: Copy + Clone { pub trait CoercePointeeValidated { /* compiler built-in */ } + +/// Allows value to be reborrowed as exclusive, creating a copy of the value +/// that disables the source for reads and writes for the lifetime of the copy. +#[lang = "reborrow"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait Reborrow { + /* compiler built-in */ +} + +/// Allows reborrowable value to be reborrowed as shared, creating a copy +/// that disables the source for writes for the lifetime of the copy. +#[lang = "coerce_shared"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait CoerceShared: Reborrow { + /* compiler built-in */ +} diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index e4d47dedb860..17c51ccaad85 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -2,7 +2,7 @@ //! runtime or const-eval processable way. use crate::any::TypeId; -use crate::intrinsics::{type_id, type_of}; +use crate::intrinsics::{self, type_id, type_of}; use crate::marker::PointeeSized; use crate::ptr::DynMetadata; @@ -14,8 +14,6 @@ pub struct Type { /// Per-type information pub kind: TypeKind, - /// Size of the type. `None` if it is unsized - pub size: Option, } /// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable] @@ -360,3 +358,22 @@ pub enum Abi { /// C-calling convention ExternC, } + +impl TypeId { + /// Returns the size of the type represented by this `TypeId`. `None` if it is unsized. + /// + /// # Examples + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// assert_eq!(const { TypeId::of::().size() }, Some(4)); + /// assert_eq!(const { TypeId::of::<[u8; 16]>().size() }, Some(16)); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn size(self) -> Option { + intrinsics::size_of_type_id(self) + } +} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index ab1ad407ee28..87dd873fdb57 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -149,7 +149,6 @@ mod index; mod index_range; mod range; -mod reborrow; mod try_trait; mod unsize; @@ -190,8 +189,6 @@ pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; -#[unstable(feature = "reborrow", issue = "145612")] -pub use self::reborrow::{CoerceShared, Reborrow}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] pub use self::try_trait::Residual; #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs deleted file mode 100644 index f83f4233a4de..000000000000 --- a/library/core/src/ops/reborrow.rs +++ /dev/null @@ -1,16 +0,0 @@ -/// Allows value to be reborrowed as exclusive, creating a copy of the value -/// that disables the source for reads and writes for the lifetime of the copy. -#[lang = "reborrow"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait Reborrow { - // Empty. -} - -/// Allows reborrowable value to be reborrowed as shared, creating a copy -/// that disables the source for writes for the lifetime of the copy. -#[lang = "coerce_shared"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait CoerceShared: Reborrow { - /// The type of this value when reborrowed as shared. - type Target: Copy; -} diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index 43fed944e928..a3b0e59278f7 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -1,3 +1,4 @@ +use core::cell::Cell; use core::num::NonZero; use core::sync::atomic::{AtomicUsize, Ordering}; use core::{array, assert_eq}; @@ -168,8 +169,6 @@ fn iterator_debug() { #[test] fn iterator_drops() { - use core::cell::Cell; - // This test makes sure the correct number of elements are dropped. The `R` // type is just a reference to a `Cell` that is incremented when an `R` is // dropped. @@ -337,8 +336,6 @@ fn drop(&mut self) { #[test] fn cell_allows_array_cycle() { - use core::cell::Cell; - #[derive(Debug)] struct B<'a> { a: [Cell>>; 2], @@ -513,7 +510,6 @@ fn array_rsplit_array_mut_out_of_bounds() { #[test] fn array_intoiter_advance_by() { - use std::cell::Cell; struct DropCounter<'a>(usize, &'a Cell); impl Drop for DropCounter<'_> { fn drop(&mut self) { @@ -566,7 +562,6 @@ fn drop(&mut self) { #[test] fn array_intoiter_advance_back_by() { - use std::cell::Cell; struct DropCounter<'a>(usize, &'a Cell); impl Drop for DropCounter<'_> { fn drop(&mut self) { @@ -718,6 +713,33 @@ fn drop(&mut self) { } } +#[cfg(not(panic = "abort"))] +#[test] +fn array_map_drops_unmapped_zst_elements_on_panic() { + use std::sync::ReentrantLock; + + static DROPPED: ReentrantLock> = ReentrantLock::new(Cell::new(0)); + + struct ZstDrop; + impl Drop for ZstDrop { + fn drop(&mut self) { + DROPPED.lock().update(|x| x + 1); + } + } + + let dropped = DROPPED.lock(); + dropped.set(0); + let array = [const { ZstDrop }; 5]; + let success = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = array.map(|x| { + drop(x); + assert_eq!(dropped.get(), 1); + }); + })); + assert!(success.is_err()); + assert_eq!(dropped.get(), 5); +} + // This covers the `PartialEq::<[T]>::eq` impl for `[T; N]` when it returns false. #[test] fn array_eq() { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c4292c2a421b..12b81fea9d27 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 2483b4c2aacd..e02077b96d35 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -80,8 +80,7 @@ struct TestStruct { reference: &'static u16, } - let Type { kind: Struct(ty), size, .. } = Type::of::() else { panic!() }; - assert!(size == Some(size_of::())); + let Type { kind: Struct(ty), .. } = Type::of::() else { panic!() }; assert!(!ty.non_exhaustive); assert!(ty.fields.len() == 3); assert!(ty.fields[0].name == "first"); @@ -93,6 +92,9 @@ struct TestStruct { assert!(ty.fields[2].name == "reference"); assert!(ty.fields[2].ty == TypeId::of::<&'static u16>()); assert!(ty.fields[2].offset == offset_of!(TestStruct, reference)); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); } const { @@ -145,13 +147,15 @@ union TestUnion { second: u16, } - let Type { kind: Union(ty), size, .. } = Type::of::() else { panic!() }; - assert!(size == Some(size_of::())); + let Type { kind: Union(ty), .. } = Type::of::() else { panic!() }; assert!(ty.fields.len() == 2); assert!(ty.fields[0].name == "first"); assert!(ty.fields[0].offset == offset_of!(TestUnion, first)); assert!(ty.fields[1].name == "second"); assert!(ty.fields[1].offset == offset_of!(TestUnion, second)); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); } const { @@ -191,8 +195,7 @@ enum E { }, } - let Type { kind: Enum(ty), size, .. } = Type::of::() else { panic!() }; - assert!(size == Some(size_of::())); + let Type { kind: Enum(ty), .. } = Type::of::() else { panic!() }; assert!(ty.variants.len() == 3); assert!(ty.variants[0].name == "Some"); @@ -206,15 +209,20 @@ enum E { assert!(ty.variants[2].name == "Foomp"); assert!(ty.variants[2].non_exhaustive); assert!(ty.variants[2].fields.len() == 2); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); } const { - let Type { kind: Enum(ty), size, .. } = Type::of::>() else { panic!() }; - assert!(size == Some(size_of::>())); + let Type { kind: Enum(ty), .. } = Type::of::>() else { panic!() }; assert!(ty.variants.len() == 2); assert!(ty.generics.len() == 1); let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[0] else { panic!() }; assert!(generic_ty == TypeId::of::()); + + let ty_id = TypeId::of::>(); + assert!(ty_id.size() == Some(size_of::>())); } } @@ -222,38 +230,48 @@ enum E { fn test_primitives() { use TypeKind::*; - let Type { kind: Bool(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(1)); + const { + let Type { kind: Bool(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Char(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); + let Type { kind: Char(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); - assert_eq!(ty.bits, 32); - assert!(ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits == 32); + assert!(ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(size_of::())); - assert_eq!(ty.bits as usize, size_of::() * 8); - assert!(ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits as usize == size_of::() * 8); + assert!(ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); - assert_eq!(ty.bits, 32); - assert!(!ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits == 32); + assert!(!ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(size_of::())); - assert_eq!(ty.bits as usize, size_of::() * 8); - assert!(!ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits as usize == size_of::() * 8); + assert!(!ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Float(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); - assert_eq!(ty.bits, 32); + let Type { kind: Float(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits == 32); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Str(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, None); + let Type { kind: Str(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let ty_id = TypeId::of::(); + assert!(ty_id.size() == None); + } } #[test] diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index a8046a5541c5..6001869dbf28 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -474,6 +474,57 @@ pub fn linger(&self) -> io::Result> { self.0.linger() } + /// Sets the value of the `SO_KEEPALIVE` option on this socket. + /// + /// If set to `true`, the operating system will periodically send keepalive + /// probes on an idle connection to verify that the remote peer is still + /// reachable. If the peer fails to respond after a system-determined number + /// of probes, the connection is considered broken and subsequent I/O calls + /// will return an error. + /// + /// This is useful for detecting dead peers on long-lived connections where + /// no application-level traffic is exchanged, such as database or SSH + /// connections. + /// + /// The timing and frequency of keepalive probes are controlled by + /// system-level settings and are not configured by this method alone. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_keepalive)] + /// + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_keepalive(true).expect("set_keepalive call failed"); + #[unstable(feature = "tcp_keepalive", issue = "155889")] + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + self.0.set_keepalive(keepalive) + } + + /// Gets the value of the `SO_KEEPALIVE` option on this socket. + /// + /// For more information about this option, see [`TcpStream::set_keepalive`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_keepalive)] + /// + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_keepalive(true).expect("set_keepalive call failed"); + /// assert_eq!(stream.keepalive().unwrap_or(false), true); + /// ``` + #[unstable(feature = "tcp_keepalive", issue = "155889")] + pub fn keepalive(&self) -> io::Result { + self.0.keepalive() + } + /// Sets the value of the `TCP_NODELAY` option on this socket. /// /// If set, this option disables the Nagle algorithm. This means that diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index af15009e665e..f6fe8e1b2353 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -844,6 +844,21 @@ fn linger() { assert_eq!(None, t!(stream.linger())); } +#[test] +#[cfg_attr(target_env = "sgx", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] +fn keepalive() { + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + let stream = t!(TcpStream::connect(&addr)); + + assert_eq!(false, t!(stream.keepalive())); + t!(stream.set_keepalive(true)); + assert_eq!(true, t!(stream.keepalive())); + t!(stream.set_keepalive(false)); + assert_eq!(false, t!(stream.keepalive())); +} + #[test] #[cfg_attr(target_env = "sgx", ignore)] fn nodelay() { diff --git a/library/std/src/path.rs b/library/std/src/path.rs index da3d9c2454a7..222bf77996c7 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1796,6 +1796,25 @@ pub fn into_os_string(self) -> OsString { self.inner } + /// Converts the `PathBuf` into a `String` if it contains valid Unicode data. + /// + /// On failure, ownership of the original `PathBuf` is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(pathbuf_into_string)] + /// use std::path::PathBuf; + /// + /// let path_buf = PathBuf::from("foo"); + /// let string = path_buf.into_string(); + /// assert_eq!(string, Ok(String::from("foo"))); + /// ``` + #[unstable(feature = "pathbuf_into_string", issue = "156203")] + pub fn into_string(self) -> Result { + self.into_os_string().into_string().map_err(PathBuf::from) + } + /// Converts this `PathBuf` into a [boxed](Box) [`Path`]. #[stable(feature = "into_boxed_path", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index 4fca658a7dca..cc8cda9aec04 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -43,6 +43,7 @@ target_os = "vexos", target_family = "wasm", target_os = "zkvm", + target_os = "trusty", ) => { mod generic; pub use generic::*; diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs index 79a528792106..be879f43b0bb 100644 --- a/library/std/src/sys/net/connection/motor.rs +++ b/library/std/src/sys/net/connection/motor.rs @@ -5,7 +5,7 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error, unsupported}; use crate::time::Duration; // We want to re-use as much of Rust's stdlib code as possible, @@ -127,6 +127,14 @@ pub fn linger(&self) -> io::Result> { moto_rt::net::linger(self.inner.as_raw_fd()).map_err(map_motor_error) } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { moto_rt::net::set_nodelay(self.inner.as_raw_fd(), nodelay).map_err(map_motor_error) } diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 5735a5db488f..c72581cf69f0 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -219,6 +219,14 @@ pub fn linger(&self) -> io::Result> { sgx_ineffective(None) } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn keepalive(&self) -> io::Result { + sgx_ineffective(false) + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { sgx_ineffective(()) } diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 7bbb3fba9ad4..09953cc515af 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -272,6 +272,15 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_KEEPALIVE, keepalive as c_int) } + } + + pub fn keepalive(&self) -> io::Result { + let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_KEEPALIVE)? }; + Ok(raw != 0) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { let value: i32 = if nodelay { 1 } else { 0 }; unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 256b99dfa987..8efe8bd78514 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -468,6 +468,14 @@ pub fn linger(&self) -> io::Result> { self.inner.linger() } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + self.inner.set_keepalive(keepalive) + } + + pub fn keepalive(&self) -> io::Result { + self.inner.keepalive() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { self.inner.set_nodelay(nodelay) } diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 7e3cd9b6a5e0..79e6c12d6ab3 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -6,6 +6,7 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; +use crate::sys::pal::unsupported; use crate::sys::{FromInner, IntoInner, abi}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; @@ -332,6 +333,14 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 04714048e3c5..936dd4c93233 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -456,6 +456,15 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE, keepalive as c_int) } + } + + pub fn keepalive(&self) -> io::Result { + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE)? }; + Ok(raw != 0) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index ca4fa343cb9a..0ea139cad6a0 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -426,6 +426,15 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + unsafe { setsockopt(self, c::SOL_SOCKET, c::SO_KEEPALIVE, keepalive as c::BOOL) } + } + + pub fn keepalive(&self) -> io::Result { + let raw: c::BOOL = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_KEEPALIVE)? }; + Ok(raw != 0) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { unsafe { setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } } diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 107a3e23733d..b32316a70990 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -112,6 +112,14 @@ pub fn linger(&self) -> io::Result> { unsupported() } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index fb18e8dec557..c019ec1c9e52 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -87,6 +87,14 @@ pub fn linger(&self) -> io::Result> { self.0 } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn keepalive(&self) -> io::Result { + self.0 + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { self.0 } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index d6c7e023e865..93cb777a7084 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -145,6 +145,14 @@ pub fn linger(&self) -> io::Result> { unsupported() } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 4df75453d1f4..521d24e3c496 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -353,6 +353,14 @@ pub fn linger(&self) -> io::Result> { unimpl!(); } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn keepalive(&self) -> io::Result { + unimpl!(); + } + pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> { crate::os::xous::ffi::blocking_scalar( services::net_server(), diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c21d1de81341..7b5abbc36378 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2388,6 +2388,7 @@ SleepConditionVariableSRW SleepEx SO_BROADCAST SO_ERROR +SO_KEEPALIVE SO_LINGER SO_RCVTIMEO SO_SNDTIMEO diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index eb54efd1c1fe..7cef71097a78 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -3189,6 +3189,7 @@ fn default() -> Self { pub const SOL_SOCKET: i32 = 65535i32; pub const SO_BROADCAST: i32 = 32i32; pub const SO_ERROR: i32 = 4103i32; +pub const SO_KEEPALIVE: i32 = 8i32; pub const SO_LINGER: i32 = 128i32; pub const SO_RCVTIMEO: i32 = 4102i32; pub const SO_SNDTIMEO: i32 = 4101i32; diff --git a/library/std/src/sys/time/windows.rs b/library/std/src/sys/time/windows.rs index 2e38dbf5cf28..b2abee4219fe 100644 --- a/library/std/src/sys/time/windows.rs +++ b/library/std/src/sys/time/windows.rs @@ -35,7 +35,14 @@ pub fn now() -> Instant { let freq = perf_counter::frequency() as u64; let now = perf_counter::now(); + + // We convert now to `u64` to be able to use `Duration`. let instant_nsec = mul_div_u64(now as u64, NANOS_PER_SEC, freq); + // We can add an arbitrary offset to shift the epoch of this clock. We do that to avoid + // being too close to 0 which would lead to underflow when computing times in the past. Also + // see . + let instant_nsec = instant_nsec + (u64::MAX / 4); + Self { t: Duration::from_nanos(instant_nsec) } } diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs index b73e7bc3962e..d6736e25ace1 100644 --- a/library/std/tests/time.rs +++ b/library/std/tests/time.rs @@ -1,4 +1,5 @@ #![feature(duration_constants)] +#![feature(duration_constructors)] #![feature(time_systemtime_limits)] #![feature(time_saturating_systemtime)] @@ -104,7 +105,8 @@ fn instant_math_is_associative() { #[test] fn instant_duration_since_saturates() { let a = Instant::now(); - assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO); + // This also checks that computing an instant before program startup works. + assert_eq!((a - Duration::from_days(1)).duration_since(a), Duration::ZERO); } #[test] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index cce6ca748ccc..22568d5f6f1f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -33,6 +33,7 @@ target_os = "psp", target_os = "solid_asp3", all(target_vendor = "fortanix", target_env = "sgx"), + all(target_os = "wasi", panic = "unwind"), ) => { mod libunwind; pub use libunwind::*; diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 091efa9c5129..ead6e0827501 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -75,7 +75,7 @@ pub enum _Unwind_Reason_Code { #[cfg(all(target_arch = "wasm32", target_os = "emscripten"))] pub const unwinder_private_data_size: usize = 20; -#[cfg(all(target_arch = "wasm32", target_os = "linux"))] +#[cfg(all(target_arch = "wasm32", any(target_os = "linux", target_os = "wasi")))] pub const unwinder_private_data_size: usize = 2; #[cfg(target_arch = "hexagon")] @@ -111,6 +111,13 @@ pub enum _Unwind_Context {} ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] +// Explicitly link the `unwind` library on WASI targets. +// +// This is provided in the self-contained sysroot for WASI targets by default. +// Note that Rust defaults to `-Cpanic=abort` on WASI targets meaning that this +// doesn't end up getting used by default, but this does mean that with +// `-Zbuild-std` this'll automatically link it in. +#[cfg_attr(target_os = "wasi", link(name = "unwind"))] unsafe extern "C-unwind" { pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 46d05b9d5d2f..68a4f928464f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -457,6 +457,16 @@ fn copy_self_contained_objects( DependencyType::TargetSelfContained, ); } + if srcdir.join("eh").exists() { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir.join("eh"), + "libunwind.a", + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } } else if target.is_windows_gnu() || target.is_windows_gnullvm() { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index fd3d129c231d..6518224576c9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1500,7 +1500,7 @@ fn default_wasi_runner(&self, target: TargetSelection) -> Option { if let Some(path) = finder.maybe_have("wasmtime") && let Ok(mut path) = path.into_os_string().into_string() { - path.push_str(" run -C cache=n --dir ."); + path.push_str(" run -Wexceptions -C cache=n --dir ."); // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is // required for libtest to work on beta/stable channels. // diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 71ff3ea3a76a..041493380129 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -87,9 +87,9 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/tmp/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/tmp/wasi-sdk-33.0-x86_64-linux COPY scripts/freebsd-toolchain.sh /tmp/ RUN /tmp/freebsd-toolchain.sh i686 diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index 23976199b8a9..acbd99db6c4f 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -21,9 +21,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-33.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 104a40403d3e..26e77bc5da20 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -43,9 +43,9 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-33.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ --set rust.lld" @@ -57,9 +57,9 @@ ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v38.0.4/wasmtime-v38.0.4-x86_64-linux.tar.xz | \ +RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v44.0.1/wasmtime-v44.0.1-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH="$PATH:/wasmtime-v38.0.4-x86_64-linux" +ENV PATH="$PATH:/wasmtime-v44.0.1-x86_64-linux" ENV WASM_WASIP_TARGET=wasm32-wasip1 ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $WASM_WASIP_TARGET \ diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index eb74edda22de..16499659291a 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -70,17 +70,16 @@ be used instead. [`wasi-libc`]: https://github.com/WebAssembly/wasi-libc -## Building the target +## Building the target in rustc -To build this target first acquire a copy of -[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 -is the minimum needed. +To build this target first acquire a copy of [`wasi-sdk`]. At this time version +33 is the minimum needed. Next configure the `WASI_SDK_PATH` environment variable to point to where this is installed. For example: ```text -export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 +export WASI_SDK_PATH=/path/to/wasi-sdk-33.0 ``` Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` @@ -103,6 +102,16 @@ Rust programs can be built for that target: rustc --target wasm32-wasip1 your-code.rs ``` +The `wasm32-wasip1` toolchain comes with a self-contained sysroot meaning that +no external compiler is required when building for this target. Users which +build a `staticlib`, however, or use an external `-Clinker`, are recommended to +use [`wasi-sdk`]. The minimum version required of [`wasi-sdk`] will change over +time as it's updated in Rust and Rust relies on newer features that [`wasi-sdk`] +has. See the documentation above about building the target in rustc for the +minimum known working version. + +[`wasi-sdk`]: https://github.com/WebAssembly/wasi-sdk + ## Cross-compilation This target can be cross-compiled from any hosts. diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index 861083ad4a61..4b7e84d71576 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -27,23 +27,20 @@ are required to support components since this target outputs a component as opposed to a core wasm module. As of the time of this writing Wasmtime 17 and above is able to run this target natively with no extra flags. -## Building the target +## Building the target in rustc -To build this target first acquire a copy of -[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 -is the minimum needed. +See the documentation for the [building the `wasm32-wasip1` target in +rustc](./wasm32-wasip1.md#building-the-target-in-rustc) for more information. The tl;dr; +is that [`wasi-sdk`] is required, and the `wasm32-wasip1` target documents the +minimum version required. -Next configure the `WASI_SDK_PATH` environment variable to point to where this -is installed. For example: +[`wasi-sdk`]: https://github.com/WebAssembly/wasi-sdk -```text -export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 -``` +## Building Rust programs -Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` -driver for LLD is required when linking WebAssembly code together. Rust's build -system will automatically pick up any necessary binaries and programs from -`WASI_SDK_PATH`. +For more information see the documentation [`wasm32-wasip1` +target](./wasm32-wasip1.md#building-rust-programs). Replace `wasm32-wasip1` +target strings with `wasm32-wasip2`, however. ## Testing diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index f4ef9f78b35c..0f5e021788c6 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -134,7 +134,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 9cc24c98a859..87e803921a85 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -30,6 +30,20 @@ pub(crate) fn line_directive<'a>( revision = Some(line_revision); raw_directive = after_close_bracket.trim_start(); + + if line_revision.contains(",") { + let suggestion: Vec<_> = line_revision + .split(",") + .map(|revision| { + format!("{COMPILETEST_DIRECTIVE_PREFIX} [{revision}]: {raw_directive}") + }) + .collect(); + panic!( + "malformed condition directive: multiple revisions aren't supported yet in `{}`, split them like\n{}", + original_line, + suggestion.join("\n"), + ); + } } else { revision = None; raw_directive = after_comment; diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 56d52982a821..c2d73f95c544 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -632,6 +632,21 @@ fn test_miropt_mode_forbidden_revisions() { parse_early_props(&config, "//@ revisions: CHECK"); } +#[test] +#[should_panic(expected = "malformed condition directive: multiple revisions aren't supported yet")] +fn test_multiple_revisions_in_directive() { + let directive = "//@ [foo,bar] compile-flags: -Z hello"; + + // The problem: this is seen as a single revision. + let line_directive = line_directive(Utf8Path::new("foo.txt"), LineNumber::ZERO, directive); + assert!(line_directive.is_some()); + assert_eq!(Some("foo,bar"), line_directive.unwrap().revision); + + // The solution for now: forbid directives from having multiple revisions. + let config: Config = cfg().build(); + parse_early_props(&config, directive); +} + #[test] fn test_forbidden_revisions_allowed_in_non_filecheck_dir() { let revisions = ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"]; diff --git a/src/tools/miri/tests/fail/layout_cycle.rs b/src/tools/miri/tests/fail/layout_cycle.rs index 8d5f1914d0c3..9e93761871d6 100644 --- a/src/tools/miri/tests/fail/layout_cycle.rs +++ b/src/tools/miri/tests/fail/layout_cycle.rs @@ -1,8 +1,7 @@ -//~ ERROR: cycle detected when computing layout of - use std::mem; pub struct S { + //~^ ERROR: cycle detected when computing layout of pub f: ::I, } diff --git a/src/tools/miri/tests/fail/layout_cycle.stderr b/src/tools/miri/tests/fail/layout_cycle.stderr index f8d555e5a10b..1efbe239016e 100644 --- a/src/tools/miri/tests/fail/layout_cycle.stderr +++ b/src/tools/miri/tests/fail/layout_cycle.stderr @@ -1,6 +1,14 @@ error[E0391]: cycle detected when computing layout of `S>` + --> tests/fail/layout_cycle.rs:LL:CC | - = note: ...which requires computing layout of ` as Tr>::I`... +LL | pub struct S { + | ^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of ` as Tr>::I`... + --> tests/fail/layout_cycle.rs:LL:CC + | +LL | type I: Tr; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `S>`, completing the cycle note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE` --> RUSTLIB/core/src/mem/mod.rs:LL:CC diff --git a/tests/assembly-llvm/wasm_exceptions.rs b/tests/assembly-llvm/wasm_exceptions.rs index 704e8026f3f4..69127462a019 100644 --- a/tests/assembly-llvm/wasm_exceptions.rs +++ b/tests/assembly-llvm/wasm_exceptions.rs @@ -33,11 +33,11 @@ pub fn test_cleanup() { } // CHECK-NOT: call - // CHECK: try + // CHECK: try_table (catch_all_ref 0) // CHECK: call may_panic - // CHECK: catch_all - // CHECK: rethrow - // CHECK: end_try + // CHECK: end_try_table + // CHECK: call log_number + // CHECK: throw_ref } // CHECK-LABEL: test_rtry: @@ -57,11 +57,11 @@ pub fn test_rtry() { } // CHECK-NOT: call - // CHECK: try + // CHECK: try_table (catch __cpp_exception 0) // CHECK: call may_panic - // CHECK: catch + // CHECK: end_try_table // CHECK: call log_number // CHECK: call log_number - // CHECK-NOT: rethrow - // CHECK: end_try + // CHECK-NOT: throw_ref + // CHECK: end_function } diff --git a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs index cbc1f24b8c16..de29abfd7c3a 100644 --- a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs +++ b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs @@ -10,7 +10,7 @@ fn main() { .env("RUSTC_STAGE", "0") .env("RUSTC", rustc_path()) // We want to disallow all nightly features to simulate a stable build - .env("RUSTFLAGS", "-Zallow-features=") + .env("RUSTFLAGS", "-D warnings -Zallow-features=") .arg("build") .arg("--manifest-path") .arg(source_root().join("Cargo.toml")) diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock new file mode 100644 index 000000000000..8d2834ad01ab --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock @@ -0,0 +1,45 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfi-types" +version = "0.0.8" + +[[package]] +name = "cross-lang-cfi-types-crate-abort" +version = "0.1.0" +dependencies = [ + "cfi-types", +] + +[[package]] +name = "cross-lang-cfi-types-crate-not-abort" +version = "0.1.0" +dependencies = [ + "cfi-types", +] + +[[package]] +name = "indirect-arity-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-pointee-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-return-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-type-qualifier-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "invalid-branch-target-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml new file mode 100644 index 000000000000..2b22762883c0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml @@ -0,0 +1,13 @@ +# Workspace mirroring the examples in . +[workspace] +resolver = "2" +members = [ + "invalid-branch-target-abort", + "indirect-arity-mismatch-abort", + "indirect-pointee-type-mismatch-abort", + "indirect-return-type-mismatch-abort", + "indirect-type-qualifier-mismatch-abort", + "indirect-type-mismatch-abort", + "cross-lang-cfi-types-crate-abort", + "cross-lang-cfi-types-crate-not-abort", +] diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml new file mode 100644 index 000000000000..2b6082a5b653 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-lang-cfi-types-crate-abort" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfi-types = { path = "../vendor/cfi-types" } diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs new file mode 100644 index 000000000000..e6a0311d2d1b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&[]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c new file mode 100644 index 000000000000..9021075763bb --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c @@ -0,0 +1,5 @@ +int +do_twice(int (*fn)(int), int arg) +{ + return fn(arg) + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs new file mode 100644 index 000000000000..dc6490f6d3ff --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs @@ -0,0 +1,36 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site, +// across the FFI boundary using the `cfi_types` crate for cross-language LLVM +// CFI. + +use std::mem; + +use cfi_types::{c_int, c_long}; + +#[link(name = "foo")] +unsafe extern "C" { + fn do_twice(f: unsafe extern "C" fn(c_int) -> c_int, arg: i32) -> i32; +} + +unsafe extern "C" fn add_one(x: c_int) -> c_int { + c_int(x.0 + 1) +} + +unsafe extern "C" fn add_two(x: c_long) -> c_long { + c_long(x.0 + 2) +} + +fn main() { + let answer = unsafe { do_twice(add_one, 5) }; + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: unsafe extern "C" fn(c_int) -> c_int = unsafe { + mem::transmute::<*const u8, unsafe extern "C" fn(c_int) -> c_int>(add_two as *const u8) + }; + let next_answer = unsafe { do_twice(f, 5) }; + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml new file mode 100644 index 000000000000..81b068d79742 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-lang-cfi-types-crate-not-abort" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfi-types = { path = "../vendor/cfi-types" } diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs new file mode 100644 index 000000000000..e6a0311d2d1b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&[]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c new file mode 100644 index 000000000000..d02bbb285883 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c @@ -0,0 +1,23 @@ +#include +#include + +// This definition has the type id "_ZTSFvlE". +void +hello_from_c(long arg) +{ + printf("Hello from C!\n"); +} + +// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the +// purposes of this example. +void +indirect_call_from_c(void (*fn)(long), long arg) +{ + // This call site tests whether the destination pointer is a member of the + // group derived from the same type id of the fn declaration, which has the + // type id "_ZTSFvlE". + // + // Notice that since the test is at the call site and generated by Clang, + // the type id used in the test is encoded by Clang. + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs new file mode 100644 index 000000000000..a9d1326d6dc4 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs @@ -0,0 +1,67 @@ +use cfi_types::c_long; + +#[link(name = "foo")] +extern "C" { + // This declaration has the type id "_ZTSFvlE" because it uses the CFI types + // for cross-language LLVM CFI support. The cfi_types crate provides a new + // set of C types as user-defined types using the cfi_encoding attribute and + // repr(transparent) to be used for cross-language LLVM CFI support. This + // new set of C types allows the Rust compiler to identify and correctly + // encode C types in extern "C" function types indirectly called across the + // FFI boundary when CFI is enabled. + fn hello_from_c(_: c_long); + + // This declaration has the type id "_ZTSFvPFvlElE" because it uses the CFI + // types for cross-language LLVM CFI support--this can be ignored for the + // purposes of this example. + fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); +} + +// This definition has the type id "_ZTSFvlE" because it uses the CFI types for +// cross-language LLVM CFI support, similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust(_: c_long) { + println!("Hello, world!"); +} + +// This definition has the type id "_ZTSFvlE" because it uses the CFI types for +// cross-language LLVM CFI support, similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust_again(_: c_long) { + println!("Hello from Rust again!"); +} + +// This definition would also have the type id "_ZTSFvPFvlElE" because it uses +// the CFI types for cross-language LLVM CFI support, similarly to the +// hello_from_c declaration above--this can be ignored for the purposes of this +// example. +fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { + // This indirect call site tests whether the destination pointer is a member + // of the group derived from the same type id of the f declaration, which + // has the type id "_ZTSFvlE" because it uses the CFI types for + // cross-language LLVM CFI support, similarly to the hello_from_c + // declaration above. + unsafe { f(arg) } +} + +// This definition has the type id "_ZTSFvvE"--this can be ignored for the +// purposes of this example. +fn main() { + // This demonstrates an indirect call within Rust-only code using the same + // encoding for hello_from_rust and the test at the indirect call site at + // indirect_call (i.e., "_ZTSFvlE"). + indirect_call(hello_from_rust, c_long(5)); + + // This demonstrates an indirect call across the FFI boundary with the Rust + // compiler and Clang using the same encoding for hello_from_c and the test + // at the indirect call site at indirect_call (i.e., "_ZTSFvlE"). + indirect_call(hello_from_c, c_long(5)); + + // This demonstrates an indirect call to a function passed as a callback + // across the FFI boundary with the Rust compiler and Clang the same + // encoding for the passed-callback declaration and the test at the indirect + // call site at indirect_call_from_c (i.e., "_ZTSFvlE"). + unsafe { + indirect_call_from_c(hello_from_rust_again, c_long(5)); + } +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock new file mode 100644 index 000000000000..5c2cbfd47ac5 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross-lang-integer-normalization-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml new file mode 100644 index 000000000000..b9044fba728a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cross-lang-integer-normalization-abort" +version = "0.1.0" +edition = "2021" + +# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can +# be built with different `RUSTFLAGS` (i.e., integer normalization). +[workspace] +members = ["."] +resolver = "2" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs new file mode 100644 index 000000000000..de04b350552a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c new file mode 100644 index 000000000000..9021075763bb --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c @@ -0,0 +1,5 @@ +int +do_twice(int (*fn)(int), int arg) +{ + return fn(arg) + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs new file mode 100644 index 000000000000..ef5d1da6ca73 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs @@ -0,0 +1,34 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site, +// across the FFI boundary using integer normalization for cross-language LLVM +// CFI. + +use std::mem; + +#[link(name = "foo")] +extern "C" { + fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32; +} + +unsafe extern "C" fn add_one(x: i32) -> i32 { + x + 1 +} + +unsafe extern "C" fn add_two(x: i64) -> i64 { + x + 2 +} + +fn main() { + let answer = unsafe { do_twice(add_one, 5) }; + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: unsafe extern "C" fn(i32) -> i32 = unsafe { + mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8) + }; + let next_answer = unsafe { do_twice(f, 5) }; + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock new file mode 100644 index 000000000000..957356f828d9 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross-lang-integer-normalization-not-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml new file mode 100644 index 000000000000..a89a3ae9b350 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cross-lang-integer-normalization-not-abort" +version = "0.1.0" +edition = "2021" + +# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can +# be built with different `RUSTFLAGS` (i.e., integer normalization). +[workspace] +members = ["."] +resolver = "2" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs new file mode 100644 index 000000000000..de04b350552a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c new file mode 100644 index 000000000000..d02bbb285883 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c @@ -0,0 +1,23 @@ +#include +#include + +// This definition has the type id "_ZTSFvlE". +void +hello_from_c(long arg) +{ + printf("Hello from C!\n"); +} + +// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the +// purposes of this example. +void +indirect_call_from_c(void (*fn)(long), long arg) +{ + // This call site tests whether the destination pointer is a member of the + // group derived from the same type id of the fn declaration, which has the + // type id "_ZTSFvlE". + // + // Notice that since the test is at the call site and generated by Clang, + // the type id used in the test is encoded by Clang. + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs new file mode 100644 index 000000000000..70a4d9a789e5 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs @@ -0,0 +1,89 @@ +use std::ffi::c_long; + +#[link(name = "foo")] +extern "C" { + // This declaration would have the type id "_ZTSFvlE", but at the time types + // are encoded, all type aliases are already resolved to their respective + // Rust aliased types, so this is encoded either as "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" depending to what type c_long type alias is resolved to, + // which currently uses the u vendor extended type + // encoding for the Rust integer types--this is the problem demonstrated in + // this example. + fn hello_from_c(_: c_long); + + // This declaration would have the type id "_ZTSFvPFvlElE", but is encoded + // either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E" + // (compressed), similarly to the hello_from_c declaration above--this can + // be ignored for the purposes of this example. + fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); +} + +// This definition would have the type id "_ZTSFvlE", but is encoded either as +// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust(_: c_long) { + println!("Hello, world!"); +} + +// This definition would have the type id "_ZTSFvlE", but is encoded either as +// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust_again(_: c_long) { + println!("Hello from Rust again!"); +} + +// This definition would also have the type id "_ZTSFvPFvlElE", but is encoded +// either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E" +// (compressed), similarly to the hello_from_c declaration above--this can be +// ignored for the purposes of this example. +fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { + // This indirect call site tests whether the destination pointer is a member + // of the group derived from the same type id of the f declaration, which + // would have the type id "_ZTSFvlE", but is encoded either as + // "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c + // declaration above. + // + // Notice that since the test is at the call site and generated by the Rust + // compiler, the type id used in the test is encoded by the Rust compiler. + unsafe { f(arg) } +} + +// This definition has the type id "_ZTSFvvE"--this can be ignored for the +// purposes of this example. +fn main() { + // This demonstrates an indirect call within Rust-only code using the same + // encoding for hello_from_rust and the test at the indirect call site at + // indirect_call (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E"). + indirect_call(hello_from_rust, 5); + + // This demonstrates an indirect call across the FFI boundary with the Rust + // compiler and Clang using different encodings for hello_from_c and the + // test at the indirect call site at indirect_call (i.e., "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" vs "_ZTSFvlE"). + // + // When using rustc LTO (i.e., -Clto), this works because the type id used + // is from the Rust-declared hello_from_c, which is encoded by the Rust + // compiler (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E"). + // + // When using (proper) LTO (i.e., -Clinker-plugin-lto), this does not work + // because the type id used is from the C-defined hello_from_c, which is + // encoded by Clang (i.e., "_ZTSFvlE"). + indirect_call(hello_from_c, 5); + + // This demonstrates an indirect call to a function passed as a callback + // across the FFI boundary with the Rust compiler and Clang using different + // encodings for the passed-callback declaration and the test at the + // indirect call site at indirect_call_from_c (i.e., "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" vs "_ZTSFvlE"). + // + // When Rust functions are passed as callbacks across the FFI boundary to be + // called back from C code, the tests are also at the call site but + // generated by Clang instead, so the type ids used in the tests are encoded + // by Clang, which will not match the type ids of declarations encoded by + // the Rust compiler (e.g., hello_from_rust_again). (The same happens the + // other way around for C functions passed as callbacks across the FFI + // boundary to be called back from Rust code.) + unsafe { + indirect_call_from_c(hello_from_rust_again, 5); + } +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..8c7f2b33265f --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-arity-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..8cc0dba58b20 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with a different number of parameters than +// arguments intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i32, _y: i32) -> i32 { + x + 2 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..0526f844363b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-pointee-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..ad0d7ddf0192 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs @@ -0,0 +1,32 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter (i.e., pointee) +// types than the return type expected and arguments intended/passed at the +// call/branch site. + +use std::mem; + +fn add_one(x: *const i32) -> i32 { + unsafe { *x + 1 } +} + +fn add_two(x: *const i64) -> i32 { + unsafe { (*x + 2) as i32 } +} + +fn do_twice(f: fn(*const i32) -> i32, arg: *const i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let value: i32 = 5; + let answer = do_twice(add_one, &value); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(*const i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(*const i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, &value); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..392975f87227 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-return-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..e21e71ee71a0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with a different return type than the return type +// expected at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i32) -> i64 { + i64::from(x + 2) +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..e167b2acfbcf --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..0f40aa316152 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i64) -> i64 { + x + 2 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..1f4b3d4b0c9b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-type-qualifier-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..e37d883addf1 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs @@ -0,0 +1,31 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with parameter type qualifiers than the argument +// type qualifiers intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: &i32) -> i32 { + *x + 1 +} + +fn add_two(x: &mut i32) -> i32 { + *x + 2 +} + +fn do_twice(f: fn(&i32) -> i32, arg: &i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let value: i32 = 5; + let answer = do_twice(add_one, &value); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(&i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(&i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, &value); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml new file mode 100644 index 000000000000..e6af74f961e0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "invalid-branch-target-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs new file mode 100644 index 000000000000..0b047bf5fefb --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs @@ -0,0 +1,50 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to an invalid destination (i.e., within the body of the +// function). + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[unsafe(naked)] +pub extern "C" fn add_two(_x: i32) -> ! { + // x + 2 preceded by a landing pad/nop block + core::arch::naked_asm!( + r#" + nop + nop + nop + nop + nop + nop + nop + nop + nop + lea eax, [rdi + 2] + ret + "#, + ); +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = unsafe { + // Offset 0 is a valid branch/call destination (i.e., the function entry + // point), but offsets 1-8 within the landing pad/nop block are invalid + // branch/call destinations (i.e., within the body of the function). + mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) + }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs new file mode 100644 index 000000000000..c85d01a71105 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs @@ -0,0 +1,151 @@ +//! Verifies that the examples in build and run with +//!`-Zbuild-std` to prevent regressions such as [rust-lang/rust#142284]. + +//@ needs-sanitizer-cfi +//@ needs-force-clang-based-tests +//@ needs-rust-lld +//@ needs-target-std +//@ ignore-cross-compile +//@ only-x86_64-unknown-linux-gnu + +#![deny(warnings)] + +use std::path::Path; + +use run_make_support::external_deps::rustc::sysroot as rustc_sysroot; +use run_make_support::run::cmd; +use run_make_support::{bin_name, cargo, path, target}; + +fn clang_path() -> String { + if let Ok(d) = std::env::var("LLVM_BIN_DIR") { + let clang = Path::new(d.trim_end_matches('/')).join("clang"); + if clang.exists() { + return clang.display().to_string(); + } + } + if let Ok(clang) = std::env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if clang.exists() { + return clang.display().to_string(); + } + } + "clang".to_string() +} + +fn fuse_ld_path() -> String { + if let Ok(d) = std::env::var("LLVM_BIN_DIR") { + let llvm_bin_dir = Path::new(d.trim_end_matches('/')); + let gcc_ld_lld = llvm_bin_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + let ld_lld = llvm_bin_dir.join("ld.lld"); + if ld_lld.exists() { + return ld_lld.display().to_string(); + } + } + if let Ok(clang) = std::env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if let Some(clang_dir) = clang.parent() { + let gcc_ld_lld = clang_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + let ld_lld = clang_dir.join("ld.lld"); + if ld_lld.exists() { + return ld_lld.display().to_string(); + } + } + } + let target_bin_dir = rustc_sysroot().join("lib").join("rustlib").join(target()).join("bin"); + let gcc_ld_lld = target_bin_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + "ld.lld".to_string() +} + +fn run_and_expect_cfi_abort(target_dir: &Path, target: &str, binary: &str) { + let exe = target_dir.join(target).join("release").join(bin_name(binary)); + let output = cmd(&exe).run_fail(); + output + .assert_stdout_contains("With CFI enabled, you should not see the next answer") + .assert_stdout_not_contains("The next answer is:"); +} + +fn run_and_expect_cfi_not_abort(target_dir: &Path, target: &str, binary: &str) { + let exe = target_dir.join(target).join("release").join(bin_name(binary)); + let output = cmd(&exe).run(); + output.assert_stdout_contains("Hello from C!"); +} + +fn main() { + let clang = clang_path(); + let fuse_ld = fuse_ld_path(); + let tgt = target(); + let target_dir = path("target"); + let lib = std::env::var("LIB").unwrap_or_default(); + + let prior_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default(); + + let rustflags = format!( + "{prior_rustflags} -Clinker-plugin-lto -Clinker={clang} \ + -Clink-arg=-fuse-ld={fuse_ld} -Zsanitizer=cfi \ + -Ctarget-feature=-crt-static" + ) + .trim() + .to_owned(); + + let rustflags_with_integer_normalization = + format!("{rustflags} -Zsanitizer-cfi-normalize-integers").trim().to_owned(); + + let run = |manifest: &Path, rustflags: &str, workspace: bool| { + let mut c = cargo(); + c.arg("build") + .arg("--manifest-path") + .arg(manifest) + .arg("--release") + .arg("-Zbuild-std") + .arg("--target") + .arg(&tgt); + if workspace { + c.arg("--workspace"); + } + c.env("RUSTFLAGS", rustflags) + .env("CC", &clang) + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .env("LIB", &lib) + .run(); + }; + + run(Path::new("Cargo.toml"), &rustflags, true); + for bin in [ + "invalid-branch-target-abort", + "indirect-arity-mismatch-abort", + "indirect-pointee-type-mismatch-abort", + "indirect-return-type-mismatch-abort", + "indirect-type-qualifier-mismatch-abort", + "indirect-type-mismatch-abort", + "cross-lang-cfi-types-crate-abort", + ] { + run_and_expect_cfi_abort(&target_dir, &tgt, bin); + } + for bin in ["cross-lang-cfi-types-crate-not-abort"] { + run_and_expect_cfi_not_abort(&target_dir, &tgt, bin); + } + + run( + Path::new("cross-lang-integer-normalization-abort/Cargo.toml"), + &rustflags_with_integer_normalization, + false, + ); + run_and_expect_cfi_abort(&target_dir, &tgt, "cross-lang-integer-normalization-abort"); + + run( + Path::new("cross-lang-integer-normalization-not-abort/Cargo.toml"), + &rustflags_with_integer_normalization, + false, + ); + run_and_expect_cfi_not_abort(&target_dir, &tgt, "cross-lang-integer-normalization-not-abort"); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs new file mode 100644 index 000000000000..2c2e0fd4ffb8 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs @@ -0,0 +1,73 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn clang_path() -> PathBuf { + if let Ok(d) = env::var("LLVM_BIN_DIR") { + let clang = Path::new(d.trim_end_matches('/')).join("clang"); + if clang.exists() { + return clang; + } + } + if let Ok(clang) = env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if clang.exists() { + return clang.to_path_buf(); + } + } + PathBuf::from("clang") +} + +fn llvm_ar_path() -> PathBuf { + if let Ok(d) = env::var("LLVM_BIN_DIR") { + let llvm_ar = Path::new(d.trim_end_matches('/')).join("llvm-ar"); + if llvm_ar.exists() { + return llvm_ar; + } + } + if let Ok(clang) = env::var("CLANG") { + let clang = Path::new(&clang); + if let Some(clang_dir) = clang.parent() { + let llvm_ar = clang_dir.join("llvm-ar"); + if llvm_ar.exists() { + return llvm_ar; + } + } + } + PathBuf::from("llvm-ar") +} + +fn build_foo_static_lib(extra_flags: &[&str]) { + let out_dir = env::var("OUT_DIR").expect("OUT_DIR"); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"); + let c_src = Path::new(&manifest_dir).join("src/foo.c"); + let bc_path = Path::new(&out_dir).join("foo.bc"); + let a_path = Path::new(&out_dir).join("libfoo.a"); + + let clang = clang_path(); + let llvm_ar = llvm_ar_path(); + + let mut clang_args = vec!["-Wall", "-flto=thin", "-fsanitize=cfi"]; + clang_args.extend_from_slice(extra_flags); + clang_args.extend_from_slice(&["-fvisibility=hidden", "-c", "-emit-llvm", "-o"]); + + let st = Command::new(&clang) + .args(&clang_args) + .arg(&bc_path) + .arg(&c_src) + .status() + .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", clang.display())); + assert!(st.success(), "`{}` failed with {st}", clang.display()); + + let st = Command::new(&llvm_ar) + .args(["rcs", a_path.to_str().unwrap(), bc_path.to_str().unwrap()]) + .status() + .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", llvm_ar.display())); + assert!(st.success(), "`{}` failed with {st}", llvm_ar.display()); + + println!("cargo:rustc-link-search=native={out_dir}"); + println!("cargo:rustc-link-lib=static=foo"); + println!("cargo:rerun-if-changed={}", c_src.display()); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../shared_build_rs.rs"); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml new file mode 100644 index 000000000000..180bfea79fa0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cfi-types" +version = "0.0.8" +edition = "2021" + +description = "CFI types for cross-language LLVM CFI support" +homepage = "https://github.com/rcvalle/rust-crate-cfi-types" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rcvalle/rust-crate-cfi-types" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs new file mode 100644 index 000000000000..436097c32fd8 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs @@ -0,0 +1,96 @@ +//! CFI types for cross-language LLVM CFI support. +//! +//! The cfi_types crate provides a new set of C types as user-defined types +//! using the cfi_encoding attribute and repr(transparent) to be used for +//! cross-language LLVM CFI support. This new set of C types allows the Rust +//! compiler to identify and correctly encode C types in extern "C" function +//! types indirectly called across the FFI boundary when CFI is enabled. +//! +//! The use of these types are optional and are recommended for when enforcement +//! and explicitness of types used across the FFI boundary and no loss of +//! granularity for cross-language LLVM CFI are preferred. +//! +//! Alternatively, the `-Zsanitizer-cfi-normalize-integers` option may be used +//! with the Clang `-fsanitize-cfi-icall-experimental-normalize-integers` option +//! for cross-language LLVM CFI support. + +#![feature(cfg_sanitizer_cfi)] +#![feature(cfi_encoding)] +#![allow(non_camel_case_types)] + +/// CFI type equivalent to Rust's core::ffi::c_char type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "c")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_char(pub core::ffi::c_char); + +/// CFI type equivalent to Rust's core::ffi::c_int type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "i")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_int(pub core::ffi::c_int); + +/// CFI type equivalent to Rust's core::ffi::c_long type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "l")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_long(pub core::ffi::c_long); + +/// CFI type equivalent to Rust's core::ffi::c_longlong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "x")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_longlong(pub core::ffi::c_longlong); + +/// CFI type equivalent to Rust's core::ffi::c_schar type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "a")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_schar(pub core::ffi::c_schar); + +/// CFI type equivalent to Rust's core::ffi::c_short type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "s")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_short(pub core::ffi::c_short); + +/// CFI type equivalent to Rust's core::ffi::c_uchar type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "h")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_uchar(pub core::ffi::c_uchar); + +/// CFI type equivalent to Rust's core::ffi::c_uint type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "j")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_uint(pub core::ffi::c_uint); + +/// CFI type equivalent to Rust's core::ffi::c_ulong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "m")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ulong(pub core::ffi::c_ulong); + +/// CFI type equivalent to Rust's core::ffi::c_ulonglong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "y")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ulonglong(pub core::ffi::c_ulonglong); + +/// CFI type equivalent to Rust's core::ffi::c_ushort type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "t")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ushort(pub core::ffi::c_ushort); diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 5dd11b0a016e..8a7cacf20e2e 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -37,7 +37,7 @@ fn target_cpu(&self, _sess: &Session) -> String { "fake_target_cpu".to_owned() } - fn codegen_crate(&self, _tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, _tcx: TyCtxt<'_>) -> Box { Box::new(CompiledModules { modules: vec![], allocator_module: None }) } @@ -46,6 +46,7 @@ fn join_codegen( ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { let codegen_results = ongoing_codegen .downcast::() diff --git a/tests/ui/associated-inherent-types/normalization-overflow.stderr b/tests/ui/associated-inherent-types/normalization-overflow.current.stderr similarity index 78% rename from tests/ui/associated-inherent-types/normalization-overflow.stderr rename to tests/ui/associated-inherent-types/normalization-overflow.current.stderr index 05aad31c5f41..e3e662788cf0 100644 --- a/tests/ui/associated-inherent-types/normalization-overflow.stderr +++ b/tests/ui/associated-inherent-types/normalization-overflow.current.stderr @@ -1,5 +1,5 @@ error: overflow evaluating associated type `T::This` - --> $DIR/normalization-overflow.rs:9:5 + --> $DIR/normalization-overflow.rs:14:5 | LL | type This = Self::This; | ^^^^^^^^^ diff --git a/tests/ui/associated-inherent-types/normalization-overflow.next.stderr b/tests/ui/associated-inherent-types/normalization-overflow.next.stderr new file mode 100644 index 000000000000..5f0939b9f776 --- /dev/null +++ b/tests/ui/associated-inherent-types/normalization-overflow.next.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `T::This normalizes-to _` + --> $DIR/normalization-overflow.rs:14:5 + | +LL | type This = Self::This; + | ^^^^^^^^^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-inherent-types/normalization-overflow.rs b/tests/ui/associated-inherent-types/normalization-overflow.rs index 4228238aa7b7..c5505578f4f0 100644 --- a/tests/ui/associated-inherent-types/normalization-overflow.rs +++ b/tests/ui/associated-inherent-types/normalization-overflow.rs @@ -1,12 +1,19 @@ -#![feature(inherent_associated_types)] +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![feature(inherent_associated_types, rustc_attrs)] #![allow(incomplete_features)] +#![rustc_no_implicit_bounds] // FIXME(fmease): I'd prefer to report a cycle error here instead of an overflow one. struct T; impl T { - type This = Self::This; //~ ERROR overflow evaluating associated type `T::This` + type This = Self::This; + //[current]~^ ERROR: overflow evaluating associated type `T::This` + //[next]~^^ ERROR: type mismatch resolving `T::This normalizes-to _` } fn main() {} diff --git a/tests/ui/closures/return-in-closure-with-fn-bound.rs b/tests/ui/closures/return-in-closure-with-fn-bound.rs new file mode 100644 index 000000000000..5e05cec98ca1 --- /dev/null +++ b/tests/ui/closures/return-in-closure-with-fn-bound.rs @@ -0,0 +1,8 @@ +// Regression test for https://github.com/rust-lang/rust/issues/155893. + +fn func(_f: impl Fn()) { + func(|| return 2) + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/closures/return-in-closure-with-fn-bound.stderr b/tests/ui/closures/return-in-closure-with-fn-bound.stderr new file mode 100644 index 000000000000..c51e6faca42b --- /dev/null +++ b/tests/ui/closures/return-in-closure-with-fn-bound.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/return-in-closure-with-fn-bound.rs:4:20 + | +LL | func(|| return 2) + | ^ expected `()`, found integer + | +note: return type inferred to be `()` here + --> $DIR/return-in-closure-with-fn-bound.rs:4:20 + | +LL | func(|| return 2) + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-18539.rs b/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs similarity index 77% rename from tests/ui/issues/issue-18539.rs rename to tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs index 66f0dabb73a2..39d4fa845714 100644 --- a/tests/ui/issues/issue-18539.rs +++ b/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18539 + //@ run-pass // Test that coercing bare fn's that return a zero sized type to // a closure doesn't cause an LLVM ERROR diff --git a/tests/ui/issues/issue-47722.rs b/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs similarity index 83% rename from tests/ui/issues/issue-47722.rs rename to tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs index da08b8addda9..c9089b970c83 100644 --- a/tests/ui/issues/issue-47722.rs +++ b/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/47722 + //@ check-pass // Tests that automatic coercions from &mut T to *mut T diff --git a/tests/ui/issues/issue-33387.rs b/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs similarity index 89% rename from tests/ui/issues/issue-33387.rs rename to tests/ui/coercion/coerce-trait-object-removes-send-bound.rs index 5d323612e411..e7186d045a15 100644 --- a/tests/ui/issues/issue-33387.rs +++ b/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/33387 + //@ run-pass #![feature(rustc_attrs)] diff --git a/tests/ui/issues/issue-41936-variance-coerce-unsized-cycle.rs b/tests/ui/coercion/variance-coerce-unsized-cycle.rs similarity index 87% rename from tests/ui/issues/issue-41936-variance-coerce-unsized-cycle.rs rename to tests/ui/coercion/variance-coerce-unsized-cycle.rs index 2a2b88410959..91e16edd645b 100644 --- a/tests/ui/issues/issue-41936-variance-coerce-unsized-cycle.rs +++ b/tests/ui/coercion/variance-coerce-unsized-cycle.rs @@ -1,6 +1,6 @@ //@ check-pass #![allow(dead_code)] -// Regression test for #41936. The coerce-unsized trait check in +// Regression test for https://github.com/rust-lang/rust/issues/41936. The coerce-unsized trait check in // coherence was using subtyping, which triggered variance // computation, which failed because it required type info for fields // that had not (yet) been computed. diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr index 01aa5e726b45..6a7f44ed3589 100644 --- a/tests/ui/consts/const-size_of-cycle.stderr +++ b/tests/ui/consts/const-size_of-cycle.stderr @@ -10,7 +10,11 @@ note: ...which requires simplifying constant for the type system `core::mem::Siz --> $SRC_DIR/core/src/mem/mod.rs:LL:COL note: ...which requires const-evaluating + checking `core::mem::SizedTypeProperties::SIZE`... --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - = note: ...which requires computing layout of `Foo`... +note: ...which requires computing layout of `Foo`... + --> $DIR/const-size_of-cycle.rs:1:1 + | +LL | struct Foo { + | ^^^^^^^^^^ = note: ...which requires computing layout of `[u8; std::mem::size_of::()]`... note: ...which requires normalizing `[u8; std::mem::size_of::()]`... --> $DIR/const-size_of-cycle.rs:2:17 diff --git a/tests/ui/consts/issue-44415.stderr b/tests/ui/consts/issue-44415.stderr index 0e3f2e6199f7..28f681371733 100644 --- a/tests/ui/consts/issue-44415.stderr +++ b/tests/ui/consts/issue-44415.stderr @@ -9,7 +9,11 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. | LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `Foo`... +note: ...which requires computing layout of `Foo`... + --> $DIR/issue-44415.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::() }]`... note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::() }]`... --> $DIR/issue-44415.rs:6:17 diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs index c8ca45370891..48a14959d8d6 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs @@ -1,3 +1,3 @@ -use std::ops::CoerceShared; //~ ERROR use of unstable library feature `reborrow` +use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr index dbbbcdf2fd57..c4c5e06778af 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 | -LL | use std::ops::CoerceShared; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::marker::CoerceShared; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-reborrow.rs b/tests/ui/feature-gates/feature-gate-reborrow.rs index 96eecfb28a10..f016f6c6bfa5 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow.rs @@ -1,3 +1,3 @@ -use std::ops::Reborrow; //~ ERROR use of unstable library feature `reborrow` +use std::marker::Reborrow; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow.stderr b/tests/ui/feature-gates/feature-gate-reborrow.stderr index 1224909f564b..5e3033f3bf1f 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow.rs:1:5 | -LL | use std::ops::Reborrow; - | ^^^^^^^^^^^^^^^^^^ +LL | use std::marker::Reborrow; + | ^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr new file mode 100644 index 000000000000..1d6ace580951 --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr @@ -0,0 +1,21 @@ +error[E0271]: type mismatch resolving `X3 normalizes-to _` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ types differ + +error[E0271]: type mismatch resolving `X1 normalizes-to _` + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:1 + | +LL | type X2 = X3; + | ^^^^^^^ types differ + +error[E0271]: type mismatch resolving `X2 normalizes-to _` + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:1 + | +LL | type X3 = X1; + | ^^^^^^^ types differ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr new file mode 100644 index 000000000000..c7adc3af02fa --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr @@ -0,0 +1,27 @@ +error[E0275]: overflow normalizing the type alias `X2` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error[E0275]: overflow normalizing the type alias `X3` + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:1 + | +LL | type X2 = X3; + | ^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error[E0275]: overflow normalizing the type alias `X1` + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:1 + | +LL | type X3 = X1; + | ^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr new file mode 100644 index 000000000000..888e1d18dea7 --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr @@ -0,0 +1,30 @@ +error[E0391]: cycle detected when expanding type alias `X1` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:11 + | +LL | type X1 = X2; + | ^^ + | +note: ...which requires expanding type alias `X2`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:11 + | +LL | type X2 = X3; + | ^^ +note: ...which requires expanding type alias `X3`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:11 + | +LL | type X3 = X1; + | ^^ + = note: ...which again requires expanding type alias `X1`, completing the cycle + = note: type aliases cannot be recursive + = help: consider using a struct, enum, or union instead to break the cycle + = help: see for more information +note: cycle used when checking that `X1` is well-formed + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr new file mode 100644 index 000000000000..888e1d18dea7 --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr @@ -0,0 +1,30 @@ +error[E0391]: cycle detected when expanding type alias `X1` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:11 + | +LL | type X1 = X2; + | ^^ + | +note: ...which requires expanding type alias `X2`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:11 + | +LL | type X2 = X3; + | ^^ +note: ...which requires expanding type alias `X3`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:11 + | +LL | type X3 = X1; + | ^^ + = note: ...which again requires expanding type alias `X1`, completing the cycle + = note: type aliases cannot be recursive + = help: consider using a struct, enum, or union instead to break the cycle + = help: see for more information +note: cycle used when checking that `X1` is well-formed + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs index 6a43c4ac7872..24e1318ca3d7 100644 --- a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs @@ -1,14 +1,23 @@ -//@ revisions: feature gated +//@ revisions: feature_old gated_old feature_new gated_new //@ ignore-parallel-frontend query cycle -#![cfg_attr(feature, feature(lazy_type_alias))] +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [feature_new] compile-flags: -Znext-solver +//@ [gated_new] compile-flags: -Znext-solver + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] +#![cfg_attr(any(feature_old, feature_new), feature(lazy_type_alias))] #![allow(incomplete_features)] type X1 = X2; -//[gated]~^ ERROR cycle detected when expanding type alias `X1` -//[feature]~^^ ERROR: overflow normalizing the type alias `X2` +//[gated_old,gated_new]~^ ERROR cycle detected when expanding type alias `X1` +//[feature_old]~^^ ERROR: overflow normalizing the type alias `X2` +//[feature_new]~^^^ ERROR: type mismatch resolving `X3 normalizes-to _` type X2 = X3; -//[feature]~^ ERROR: overflow normalizing the type alias `X3` +//[feature_old]~^ ERROR: overflow normalizing the type alias `X3` +//[feature_new]~^^ ERROR: type mismatch resolving `X1 normalizes-to _` type X3 = X1; -//[feature]~^ ERROR: overflow normalizing the type alias `X1` +//[feature_old]~^ ERROR: overflow normalizing the type alias `X1` +//[feature_new]~^^ ERROR: type mismatch resolving `X2 normalizes-to _` fn main() {} diff --git a/tests/ui/layout/layout-cycle.rs b/tests/ui/layout/layout-cycle.rs index 846ce0882cad..e873fcdaa97a 100644 --- a/tests/ui/layout/layout-cycle.rs +++ b/tests/ui/layout/layout-cycle.rs @@ -1,11 +1,11 @@ //@ build-fail -//~^ ERROR: cycle detected when computing layout of // Issue #111176 -- ensure that we do not emit ICE on layout cycles use std::mem; pub struct S { + //~^ ERROR: cycle detected when computing layout of pub f: ::I, } diff --git a/tests/ui/layout/layout-cycle.stderr b/tests/ui/layout/layout-cycle.stderr index 28c35d431226..0652f032c9db 100644 --- a/tests/ui/layout/layout-cycle.stderr +++ b/tests/ui/layout/layout-cycle.stderr @@ -1,6 +1,14 @@ error[E0391]: cycle detected when computing layout of `S>` + --> $DIR/layout-cycle.rs:7:1 | - = note: ...which requires computing layout of ` as Tr>::I`... +LL | pub struct S { + | ^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of ` as Tr>::I`... + --> $DIR/layout-cycle.rs:13:5 + | +LL | type I: Tr; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `S>`, completing the cycle note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL diff --git a/tests/ui/layout/post-mono-layout-cycle.rs b/tests/ui/layout/post-mono-layout-cycle.rs index 841fc30a50bc..71ccc8de94da 100644 --- a/tests/ui/layout/post-mono-layout-cycle.rs +++ b/tests/ui/layout/post-mono-layout-cycle.rs @@ -1,5 +1,4 @@ //@ build-fail -//~^ ERROR cycle detected when computing layout of `Wrapper<()>` trait Trait { type Assoc; @@ -10,6 +9,7 @@ impl Trait for () { } struct Wrapper { + //~^ ERROR cycle detected when computing layout of `Wrapper<()>` _x: ::Assoc, } diff --git a/tests/ui/layout/post-mono-layout-cycle.stderr b/tests/ui/layout/post-mono-layout-cycle.stderr index b9b1b988499e..05c57d20b8fe 100644 --- a/tests/ui/layout/post-mono-layout-cycle.stderr +++ b/tests/ui/layout/post-mono-layout-cycle.stderr @@ -1,8 +1,17 @@ error[E0391]: cycle detected when computing layout of `Wrapper<()>` + --> $DIR/post-mono-layout-cycle.rs:11:1 | - = note: ...which requires computing layout of `<() as Trait>::Assoc`... +LL | struct Wrapper { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of `<() as Trait>::Assoc`... + --> $DIR/post-mono-layout-cycle.rs:4:5 + | +LL | type Assoc; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `Wrapper<()>`, completing the cycle - = note: cycle used when computing layout of `core::option::Option>` +note: cycle used when computing layout of `core::option::Option>` + --> $SRC_DIR/core/src/option.rs:LL:COL = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr index 700083069cb0..c9c15e2d62c9 100644 --- a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr @@ -6,7 +6,7 @@ note: inside `TypeId::trait_info_of::` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` diff --git a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr index 700083069cb0..c9c15e2d62c9 100644 --- a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr @@ -6,7 +6,7 @@ note: inside `TypeId::trait_info_of::` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` diff --git a/tests/ui/pattern/non-structural-match-types-cycle-err.stderr b/tests/ui/pattern/non-structural-match-types-cycle-err.stderr index 2f4ac63fc570..a7dd597d9614 100644 --- a/tests/ui/pattern/non-structural-match-types-cycle-err.stderr +++ b/tests/ui/pattern/non-structural-match-types-cycle-err.stderr @@ -14,8 +14,13 @@ note: ...which requires const-evaluating + checking ` = None; | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `core::option::Option<{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}>`... - = note: ...which requires computing layout of `{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}`... +note: ...which requires computing layout of `core::option::Option<{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}>`... + --> $SRC_DIR/core/src/option.rs:LL:COL +note: ...which requires computing layout of `{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}`... + --> $DIR/non-structural-match-types-cycle-err.rs:18:16 + | +LL | match Some(async {}) { + | ^^^^^ note: ...which requires optimizing MIR for `defines::{closure#0}`... --> $DIR/non-structural-match-types-cycle-err.rs:18:16 | diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs new file mode 100644 index 000000000000..80689d81d0cc --- /dev/null +++ b/tests/ui/reborrow/custom_marker.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let _ = method(a); + let _ = method(a); +} diff --git a/tests/ui/reborrow/custom_marker_assign_deref.rs b/tests/ui/reborrow/custom_marker_assign_deref.rs new file mode 100644 index 000000000000..79ea2a35acda --- /dev/null +++ b/tests/ui/reborrow/custom_marker_assign_deref.rs @@ -0,0 +1,26 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +impl<'a> std::ops::Deref for CustomMarker<'a> { + type Target = CustomMarker<'a>; + fn deref(&self) -> &Self::Target { + self + } +} + +impl<'a> std::ops::DerefMut for CustomMarker<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self + } +} + +fn main() { + let mut a = CustomMarker(PhantomData); + + *a = CustomMarker(PhantomData); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared.rs b/tests/ui/reborrow/custom_marker_coerce_shared.rs new file mode 100644 index 000000000000..17c7bac98d17 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared.rs @@ -0,0 +1,22 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (b, c); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs new file mode 100644 index 000000000000..56bc1f896da0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -0,0 +1,22 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (&a, b, c); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs new file mode 100644 index 000000000000..532d13da258c --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (a, b, c); + //~^ ERROR: cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr new file mode 100644 index 000000000000..90382af3ce30 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_coerce_shared_move.rs:19:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let c = method(a); +LL | let _ = (a, b, c); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_deref.rs b/tests/ui/reborrow/custom_marker_deref.rs new file mode 100644 index 000000000000..74b9bac22ed0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let mut a = CustomMarker(PhantomData); + let b = &mut a; + let _ = method(*b); +} diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.rs b/tests/ui/reborrow/custom_marker_mut_a_b.rs new file mode 100644 index 000000000000..3baf320b583b --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + //~^ ERROR: cannot borrow `a` as mutable more than once at a time + let _ = (b, c); +} diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.stderr b/tests/ui/reborrow/custom_marker_mut_a_b.stderr new file mode 100644 index 000000000000..36e3bc291803 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.stderr @@ -0,0 +1,14 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_marker_mut_a_b.rs:14:20 + | +LL | let b = method(a); + | - first mutable borrow occurs here +LL | let c = method(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = (b, c); + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/reborrow/custom_marker_mut_self.rs b/tests/ui/reborrow/custom_marker_mut_self.rs new file mode 100644 index 000000000000..a688f503517d --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.rs @@ -0,0 +1,15 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let _ = (a, b); //~ERROR cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self.stderr b/tests/ui/reborrow/custom_marker_mut_self.stderr new file mode 100644 index 000000000000..77262eed339d --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.stderr @@ -0,0 +1,15 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self.rs:14:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.rs b/tests/ui/reborrow/custom_marker_mut_self_a.rs new file mode 100644 index 000000000000..f4cc8defb05e --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.rs @@ -0,0 +1,18 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let _ = method(a); + //~^ ERROR: cannot borrow `a` as mutable more than once at a time + let _ = (a, b); + //~^ ERROR: cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.stderr b/tests/ui/reborrow/custom_marker_mut_self_a.stderr new file mode 100644 index 000000000000..4241b6ec15a2 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.stderr @@ -0,0 +1,28 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_marker_mut_self_a.rs:14:20 + | +LL | let b = method(a); + | - first mutable borrow occurs here +LL | let _ = method(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = (a, b); + | - first borrow later used here + +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self_a.rs:16:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +... +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0499, E0505. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.rs b/tests/ui/reborrow/custom_marker_mut_self_b.rs new file mode 100644 index 000000000000..16356954908b --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let _ = method(a); + let b = method(a); + let _ = (a, b); + //~^ ERROR: cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.stderr b/tests/ui/reborrow/custom_marker_mut_self_b.stderr new file mode 100644 index 000000000000..adca4331f1bb --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self_b.rs:15:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let _ = method(a); +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.rs b/tests/ui/reborrow/custom_marker_two_lifetimes.rs new file mode 100644 index 000000000000..d03282145054 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_two_lifetimes.rs @@ -0,0 +1,8 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a, 'b>(PhantomData<&'a mut ()>, PhantomData<&'b ()>); +impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} +//~^ ERROR: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + +fn main() {} diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.stderr b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr new file mode 100644 index 000000000000..ce5c4d09aeb7 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr @@ -0,0 +1,8 @@ +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/custom_marker_two_lifetimes.rs:5:1 + | +LL | impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/custom_mut.rs b/tests/ui/reborrow/custom_mut.rs index 1e7c46932382..39b5ed490610 100644 --- a/tests/ui/reborrow/custom_mut.rs +++ b/tests/ui/reborrow/custom_mut.rs @@ -1,13 +1,16 @@ -#![feature(reborrow)] -use std::ops::Reborrow; +//@ run-pass +#![feature(reborrow)] +use std::marker::Reborrow; + +#[allow(unused)] struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} -fn method(a: CustomMut<'_, ()>) {} +fn method(_: CustomMut<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); let _ = method(a); - let _ = method(a); //~ERROR use of moved value: `a` + let _ = method(a); } diff --git a/tests/ui/reborrow/custom_mut.stderr b/tests/ui/reborrow/custom_mut.stderr deleted file mode 100644 index 3b3f47b62d6f..000000000000 --- a/tests/ui/reborrow/custom_mut.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0382]: use of moved value: `a` - --> $DIR/custom_mut.rs:12:20 - | -LL | let a = CustomMut(&mut ()); - | - move occurs because `a` has type `CustomMut<'_, ()>`, which does not implement the `Copy` trait -LL | let _ = method(a); - | - value moved here -LL | let _ = method(a); - | ^ value used here after move - | -note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary - --> $DIR/custom_mut.rs:7:14 - | -LL | fn method(a: CustomMut<'_, ()>) {} - | ------ ^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value - | | - | in this function -note: if `CustomMut<'_, ()>` implemented `Clone`, you could clone the value - --> $DIR/custom_mut.rs:4:1 - | -LL | struct CustomMut<'a, T>(&'a mut T); - | ^^^^^^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let _ = method(a); - | - you could clone this value - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index e2d25835c093..dcc02db5802b 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -1,11 +1,12 @@ -#![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; +//@ run-pass +#![feature(reborrow)] +use std::marker::{CoerceShared, Reborrow}; + +#[allow(unused)] struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} -impl<'a, T> CoerceShared for CustomMut<'a, T> { - type Target = CustomRef<'a, T>; -} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} struct CustomRef<'a, T>(&'a T); @@ -16,13 +17,9 @@ fn clone(&self) -> Self { } impl<'a, T> Copy for CustomRef<'a, T> {} -fn method(a: CustomRef<'_, ()>) {} //~NOTE function defined here +fn method(_a: CustomRef<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); method(a); - //~^ ERROR mismatched types - //~| NOTE expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` - //~| NOTE arguments to this function are incorrect - //~| NOTE expected struct `CustomRef<'_, ()>` } diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.stderr b/tests/ui/reborrow/custom_mut_coerce_shared.stderr deleted file mode 100644 index 508651badc0a..000000000000 --- a/tests/ui/reborrow/custom_mut_coerce_shared.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/custom_mut_coerce_shared.rs:23:12 - | -LL | method(a); - | ------ ^ expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` - | | - | arguments to this function are incorrect - | - = note: expected struct `CustomRef<'_, ()>` - found struct `CustomMut<'_, ()>` -note: function defined here - --> $DIR/custom_mut_coerce_shared.rs:19:4 - | -LL | fn method(a: CustomRef<'_, ()>) {} - | ^^^^^^ -------------------- - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs index 6c7fc4beb543..a7eeccbfbd54 100644 --- a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs +++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs @@ -1,9 +1,8 @@ -//~ ERROR cycle detected when computing layout of `core::option::Option` -//~| NOTE see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -//~| NOTE ...which requires computing layout of `S`... -//~| NOTE ...which requires computing layout of `core::option::Option<::It>`... -//~| NOTE ...which again requires computing layout of `core::option::Option`, completing the cycle -//~| NOTE cycle used when computing layout of `core::option::Option<::It>` +//~? ERROR cycle detected when computing layout of `core::option::Option` +//~? NOTE see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information +//~? NOTE ...which requires computing layout of `core::option::Option<::It>`... +//~? NOTE ...which again requires computing layout of `core::option::Option`, completing the cycle +//~? NOTE cycle used when computing layout of `core::option::Option<::It>` trait Mirror { type It: ?Sized; @@ -12,6 +11,7 @@ impl Mirror for T { type It = Self; } struct S(Option<::It>); +//~^ NOTE ...which requires computing layout of `S`... fn main() { let _s = S(None); diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr index e77fb025bcf1..daf9240ec5c9 100644 --- a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -1,9 +1,16 @@ error[E0391]: cycle detected when computing layout of `core::option::Option` + --> $SRC_DIR/core/src/option.rs:LL:COL | - = note: ...which requires computing layout of `S`... - = note: ...which requires computing layout of `core::option::Option<::It>`... +note: ...which requires computing layout of `S`... + --> $DIR/issue-26548-recursion-via-normalize.rs:13:1 + | +LL | struct S(Option<::It>); + | ^^^^^^^^ +note: ...which requires computing layout of `core::option::Option<::It>`... + --> $SRC_DIR/core/src/option.rs:LL:COL = note: ...which again requires computing layout of `core::option::Option`, completing the cycle - = note: cycle used when computing layout of `core::option::Option<::It>` +note: cycle used when computing layout of `core::option::Option<::It>` + --> $SRC_DIR/core/src/option.rs:LL:COL = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/reflection/trait_info_of_too_big.stderr b/tests/ui/reflection/trait_info_of_too_big.stderr index fa41d3c9ec90..b5dc1d3cdb44 100644 --- a/tests/ui/reflection/trait_info_of_too_big.stderr +++ b/tests/ui/reflection/trait_info_of_too_big.stderr @@ -6,7 +6,7 @@ LL | TypeId::of::<[u8; usize::MAX]>().trait_info_of_trait_type_id(TypeId::of | note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture @@ -19,7 +19,7 @@ note: inside `TypeId::trait_info_of::` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL error: aborting due to 2 previous errors diff --git a/tests/ui/rmeta/no_optimized_mir.rs b/tests/ui/rmeta/no_optimized_mir.rs index dbf612cd03cc..c8ed00b039b2 100644 --- a/tests/ui/rmeta/no_optimized_mir.rs +++ b/tests/ui/rmeta/no_optimized_mir.rs @@ -10,5 +10,4 @@ fn main() { rmeta_meta::missing_optimized_mir(); } -//~? ERROR crate `rmeta_meta` required to be available in rlib format, but was not found in this form //~? ERROR missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` diff --git a/tests/ui/rmeta/no_optimized_mir.stderr b/tests/ui/rmeta/no_optimized_mir.stderr index 91aa98172fe5..254f100aa7b5 100644 --- a/tests/ui/rmeta/no_optimized_mir.stderr +++ b/tests/ui/rmeta/no_optimized_mir.stderr @@ -1,5 +1,3 @@ -error: crate `rmeta_meta` required to be available in rlib format, but was not found in this form - error: missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` | note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled with `--emit=metadata`?) @@ -8,5 +6,5 @@ note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled w LL | pub fn missing_optimized_mir() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/sized/recursive-type-binding.rs b/tests/ui/sized/recursive-type-binding.rs index 52de04afd66d..c67e8369a3b7 100644 --- a/tests/ui/sized/recursive-type-binding.rs +++ b/tests/ui/sized/recursive-type-binding.rs @@ -1,12 +1,14 @@ //@ build-fail -//~^ ERROR cycle detected when computing layout of `Foo<()>` -trait A { type Assoc: ?Sized; } +trait A { + type Assoc: ?Sized; +} impl A for () { type Assoc = Foo<()>; } struct Foo(T::Assoc); +//~^ ERROR cycle detected when computing layout of `Foo<()>` fn main() { let x: Foo<()>; diff --git a/tests/ui/sized/recursive-type-binding.stderr b/tests/ui/sized/recursive-type-binding.stderr index d9c2efa4d53b..290262c16a07 100644 --- a/tests/ui/sized/recursive-type-binding.stderr +++ b/tests/ui/sized/recursive-type-binding.stderr @@ -1,9 +1,17 @@ error[E0391]: cycle detected when computing layout of `Foo<()>` + --> $DIR/recursive-type-binding.rs:10:1 | - = note: ...which requires computing layout of `<() as A>::Assoc`... +LL | struct Foo(T::Assoc); + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of `<() as A>::Assoc`... + --> $DIR/recursive-type-binding.rs:4:5 + | +LL | type Assoc: ?Sized; + | ^^^^^^^^^^^^^^^^^^ = note: ...which again requires computing layout of `Foo<()>`, completing the cycle note: cycle used when elaborating drops for `main` - --> $DIR/recursive-type-binding.rs:11:1 + --> $DIR/recursive-type-binding.rs:13:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/sized/recursive-type-coercion-from-never.rs b/tests/ui/sized/recursive-type-coercion-from-never.rs index 7bd87ae06c5e..7bb535bb20a7 100644 --- a/tests/ui/sized/recursive-type-coercion-from-never.rs +++ b/tests/ui/sized/recursive-type-coercion-from-never.rs @@ -1,15 +1,17 @@ //@ build-fail -//~^ ERROR cycle detected when computing layout of `Foo<()>` // Regression test for a stack overflow: https://github.com/rust-lang/rust/issues/113197 -trait A { type Assoc; } +trait A { + type Assoc; +} impl A for () { type Assoc = Foo<()>; } struct Foo(T::Assoc); +//~^ ERROR cycle detected when computing layout of `Foo<()>` fn main() { Foo::<()>(todo!()); diff --git a/tests/ui/sized/recursive-type-coercion-from-never.stderr b/tests/ui/sized/recursive-type-coercion-from-never.stderr index 7580e780dda5..1e04c2ab396f 100644 --- a/tests/ui/sized/recursive-type-coercion-from-never.stderr +++ b/tests/ui/sized/recursive-type-coercion-from-never.stderr @@ -1,9 +1,17 @@ error[E0391]: cycle detected when computing layout of `Foo<()>` + --> $DIR/recursive-type-coercion-from-never.rs:13:1 | - = note: ...which requires computing layout of `<() as A>::Assoc`... +LL | struct Foo(T::Assoc); + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of `<() as A>::Assoc`... + --> $DIR/recursive-type-coercion-from-never.rs:6:5 + | +LL | type Assoc; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `Foo<()>`, completing the cycle note: cycle used when elaborating drops for `main` - --> $DIR/recursive-type-coercion-from-never.rs:14:1 + --> $DIR/recursive-type-coercion-from-never.rs:16:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs index d6522e3cfb64..ff080eb19d03 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.rs +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -2,11 +2,11 @@ // issue: rust-lang/rust#98842 //@ check-fail //@ edition:2021 -//~^^^^ ERROR cycle detected when computing layout of `Foo` // If the inner `Foo` is named through an associated type, // the "infinite size" error does not occur. struct Foo(<&'static Foo as ::core::ops::Deref>::Target); +//~^ ERROR cycle detected when computing layout of `Foo` // But Rust will be unable to know whether `Foo` is sized or not, // and it will infinitely recurse somewhere trying to figure out the // size of this pointer (is my guess): diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr index 5557a6fc45b8..f15d95ea9480 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr @@ -1,6 +1,11 @@ error[E0391]: cycle detected when computing layout of `Foo` + --> $DIR/stack-overflow-trait-infer-98842.rs:8:1 | - = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... +LL | struct Foo(<&'static Foo as ::core::ops::Deref>::Target); + | ^^^^^^^^^^ + | +note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL = note: ...which again requires computing layout of `Foo`, completing the cycle note: cycle used when const-evaluating + checking `_` --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 diff --git a/tests/ui/traits/assoc-type-hrtb-normalization-30472.rs b/tests/ui/traits/normalize/assoc-type-hrtb-normalization-30472.rs similarity index 100% rename from tests/ui/traits/assoc-type-hrtb-normalization-30472.rs rename to tests/ui/traits/normalize/assoc-type-hrtb-normalization-30472.rs diff --git a/tests/ui/traits/copy-impl-cannot-normalize.rs b/tests/ui/traits/normalize/copy-impl-cannot-normalize.rs similarity index 100% rename from tests/ui/traits/copy-impl-cannot-normalize.rs rename to tests/ui/traits/normalize/copy-impl-cannot-normalize.rs diff --git a/tests/ui/traits/copy-impl-cannot-normalize.stderr b/tests/ui/traits/normalize/copy-impl-cannot-normalize.stderr similarity index 100% rename from tests/ui/traits/copy-impl-cannot-normalize.stderr rename to tests/ui/traits/normalize/copy-impl-cannot-normalize.stderr diff --git a/tests/ui/traits/deep-norm-pending.rs b/tests/ui/traits/normalize/deep-norm-pending.rs similarity index 100% rename from tests/ui/traits/deep-norm-pending.rs rename to tests/ui/traits/normalize/deep-norm-pending.rs diff --git a/tests/ui/traits/deep-norm-pending.stderr b/tests/ui/traits/normalize/deep-norm-pending.stderr similarity index 100% rename from tests/ui/traits/deep-norm-pending.stderr rename to tests/ui/traits/normalize/deep-norm-pending.stderr diff --git a/tests/ui/traits/normalize-associated-type-in-where-clause.rs b/tests/ui/traits/normalize/normalize-associated-type-in-where-clause.rs similarity index 100% rename from tests/ui/traits/normalize-associated-type-in-where-clause.rs rename to tests/ui/traits/normalize/normalize-associated-type-in-where-clause.rs diff --git a/tests/ui/traits/normalize-conflicting-impls.rs b/tests/ui/traits/normalize/normalize-conflicting-impls.rs similarity index 100% rename from tests/ui/traits/normalize-conflicting-impls.rs rename to tests/ui/traits/normalize/normalize-conflicting-impls.rs diff --git a/tests/ui/traits/normalize-conflicting-impls.stderr b/tests/ui/traits/normalize/normalize-conflicting-impls.stderr similarity index 100% rename from tests/ui/traits/normalize-conflicting-impls.stderr rename to tests/ui/traits/normalize/normalize-conflicting-impls.stderr diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs b/tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs new file mode 100644 index 000000000000..c359f8363e53 --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs @@ -0,0 +1,25 @@ +// Ensure that defaulted type parameters whose default contains diverging aliases are properly +// caught with both solvers. This is currently not the case, and this is tracked in issue +// https://github.com/rust-lang/rust/issues/156271. +// MCVE from https://github.com/rust-lang/trait-system-refactor-initiative/issues/139#issuecomment-2704576249. + +//@ revisions: current next +//@ check-pass +//@ known-bug: #156271 +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] + +trait Trait { + type Diverges; +} + +impl Trait for T { + type Diverges = D::Diverges; +} + +struct Bar::Diverges>(*mut T); + +fn main() {} diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr new file mode 100644 index 000000000000..7787d6b68f25 --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr @@ -0,0 +1,9 @@ +error[E0275]: overflow evaluating the requirement `::Diverges == _` + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 + | +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr new file mode 100644 index 000000000000..d7046d2b058b --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr @@ -0,0 +1,18 @@ +error[E0271]: type mismatch resolving `::Diverges normalizes-to _` + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 + | +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `::Diverges normalizes-to _` + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 + | +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | +note: required by a bound in `Box` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs new file mode 100644 index 000000000000..45b2cb56c25f --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs @@ -0,0 +1,27 @@ +// Ensure that structs with fields whose types contain diverging aliases are properly caught with +// both solvers. +// MCVE from https://github.com/rust-lang/trait-system-refactor-initiative/issues/139#issuecomment-2703127026. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] + +trait Trait { + type Diverges; +} + +impl Trait for T { + type Diverges = D::Diverges; +} + +struct Foo { + field: Box<::Diverges>, + //[current]~^ ERROR: overflow evaluating the requirement `::Diverges == _` + //[next]~^^ ERROR: type mismatch resolving `::Diverges normalizes-to _` + //[next]~| ERROR: type mismatch resolving `::Diverges normalizes-to _` +} + +fn main() {} diff --git a/tests/ui/traits/normalize-supertrait.rs b/tests/ui/traits/normalize/normalize-supertrait.rs similarity index 100% rename from tests/ui/traits/normalize-supertrait.rs rename to tests/ui/traits/normalize/normalize-supertrait.rs diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/normalize/pointee-normalize-equate.rs similarity index 100% rename from tests/ui/traits/pointee-normalize-equate.rs rename to tests/ui/traits/normalize/pointee-normalize-equate.rs diff --git a/tests/ui/traits/self-referential-param-env-normalization.rs b/tests/ui/traits/normalize/self-referential-param-env-normalization.rs similarity index 100% rename from tests/ui/traits/self-referential-param-env-normalization.rs rename to tests/ui/traits/normalize/self-referential-param-env-normalization.rs diff --git a/tests/ui/traits/self-referential-param-env-normalization.stderr b/tests/ui/traits/normalize/self-referential-param-env-normalization.stderr similarity index 100% rename from tests/ui/traits/self-referential-param-env-normalization.stderr rename to tests/ui/traits/normalize/self-referential-param-env-normalization.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.current.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization-2.current.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization-2.current.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization-2.current.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization-2.next.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization-2.next.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization-2.next.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.rs b/tests/ui/traits/normalize/unconstrained-projection-normalization-2.rs similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization-2.rs rename to tests/ui/traits/normalize/unconstrained-projection-normalization-2.rs diff --git a/tests/ui/traits/unconstrained-projection-normalization.current.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization.current.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization.current.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization.current.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization.next.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization.next.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization.next.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization.next.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization.rs b/tests/ui/traits/normalize/unconstrained-projection-normalization.rs similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization.rs rename to tests/ui/traits/normalize/unconstrained-projection-normalization.rs diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr index 50dcea0bfac6..d429b08c8c98 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr @@ -1,8 +1,20 @@ error[E0391]: cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` + --> $DIR/129541-recursive-enum-and-array-impl.rs:8:5 | - = note: ...which requires computing layout of `Hello`... +LL | type Assoc; + | ^^^^^^^^^^ + | +note: ...which requires computing layout of `Hello`... + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: ...which again requires computing layout of `<[Hello] as Normalize>::Assoc`, completing the cycle - = note: cycle used when computing layout of `Hello` +note: cycle used when computing layout of `Hello` + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr index 50dcea0bfac6..d429b08c8c98 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr @@ -1,8 +1,20 @@ error[E0391]: cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` + --> $DIR/129541-recursive-enum-and-array-impl.rs:8:5 | - = note: ...which requires computing layout of `Hello`... +LL | type Assoc; + | ^^^^^^^^^^ + | +note: ...which requires computing layout of `Hello`... + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: ...which again requires computing layout of `<[Hello] as Normalize>::Assoc`, completing the cycle - = note: cycle used when computing layout of `Hello` +note: cycle used when computing layout of `Hello` + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs index 5b7bf5f3404c..6c0411135dad 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs @@ -1,6 +1,4 @@ // Regression test for #129541 -//~^ ERROR cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` [E0391] - //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver @@ -8,6 +6,7 @@ trait Bound {} trait Normalize { type Assoc; + //~^ ERROR cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` [E0391] } impl Normalize for T { diff --git a/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr b/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr index 13ae6dfcaa35..8b6d11b8df85 100644 --- a/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr +++ b/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr @@ -9,7 +9,11 @@ note: ...which requires const-evaluating + checking `Alpha::V3::{constant#0}`... | LL | V3 = Self::V1 {} as u8 + 2, | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `Alpha`... +note: ...which requires computing layout of `Alpha`... + --> $DIR/self-in-enum-definition.rs:2:1 + | +LL | enum Alpha { + | ^^^^^^^^^^ = note: ...which again requires simplifying constant for the type system `Alpha::V3::{constant#0}`, completing the cycle note: cycle used when checking that `Alpha` is well-formed --> $DIR/self-in-enum-definition.rs:2:1