Merge ref '63b1dfc0e00f' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: rust-lang/rust@63b1dfc0e0
Filtered ref: rust-lang/miri@bdd7780daf
Upstream diff: https://github.com/rust-lang/rust/compare/32bd660612bf1c61bdf290a3ec643c8538b8357d...63b1dfc0e00fd6f8ad7cd8817fc712e7d9b7be59

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
The Miri Cronjob Bot
2026-05-08 05:48:02 +00:00
301 changed files with 4173 additions and 1343 deletions
+1 -1
View File
@@ -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",
+46 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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,
+31
View File
@@ -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 => (
@@ -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 => (
@@ -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<UserTypeAnnotationInde
| Rvalue::ThreadLocalRef(..)
| Rvalue::Repeat(..)
| Rvalue::Ref(..)
| Rvalue::Reborrow(..)
| Rvalue::RawPtr(..)
| Rvalue::Cast(..)
| Rvalue::BinaryOp(..)
@@ -2422,6 +2432,116 @@ fn add_reborrow_constraint(
}
}
fn add_generic_reborrow_constraint(
&mut self,
mutability: Mutability,
location: Location,
borrowed_place: &Place<'tcx>,
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 `<T as CoerceShared>::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::<Vec<_>>();
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>,
@@ -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
@@ -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
@@ -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);
@@ -150,7 +150,6 @@ fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
fn emit_cgu(
output_filenames: &OutputFilenames,
invocation_temp: Option<&str>,
prof: &SelfProfilerRef,
name: String,
module: UnwindModule<ObjectModule>,
@@ -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<ModuleCodegenResult, String> {
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<CompiledModule> {
match emit_module(
tcx.output_filenames(()),
tcx.sess.invocation_temp.as_deref(),
&tcx.sess.prof,
product.object,
ModuleKind::Allocator,
@@ -33,14 +33,15 @@ fn create_jit_module(
(jit_module, cx)
}
pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec<String>) -> ! {
pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, jit_args: Vec<String>) -> ! {
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;
@@ -185,7 +185,6 @@ pub(crate) fn compile_global_asm(
config: &GlobalAsmConfig,
cgu_name: &str,
global_asm: String,
invocation_temp: Option<&str>,
) -> Result<Option<PathBuf>, 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",
);
+3 -2
View File
@@ -209,12 +209,12 @@ fn target_cpu(&self, sess: &Session) -> String {
.to_owned()
}
fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box<dyn Any> {
fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
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<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
_crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs)
}
+5 -21
View File
@@ -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(),
)
}
+4 -3
View File
@@ -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<dyn Any> {
Box::new(codegen_crate(self.clone(), tcx, crate_info))
fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
Box::new(codegen_crate(self.clone(), tcx))
}
fn join_codegen(
@@ -300,11 +300,12 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
sess: &Session,
_outputs: &OutputFilenames,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
.expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
.join(sess)
.join(sess, crate_info)
}
fn target_config(&self, sess: &Session) -> TargetConfig {
+11 -40
View File
@@ -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(),
)
}
@@ -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))
+3 -2
View File
@@ -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 <N x elem> v0, <N x elem> poison, <N x i32> 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);
}
+4 -3
View File
@@ -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<dyn Any> {
Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info))
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx))
}
fn join_codegen(
@@ -342,11 +342,12 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
let (compiled_modules, work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
.join(sess);
.join(sess, crate_info);
if sess.opts.unstable_opts.llvm_time_trace {
sess.time("llvm_dump_timing_file", || {
+1 -6
View File
@@ -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");
+1 -1
View File
@@ -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<String> {
+46 -97
View File
@@ -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<CrateType>,
pub output_filenames: Arc<OutputFilenames>,
pub invocation_temp: Option<String>,
pub module_config: Arc<ModuleConfig>,
pub opt_level: OptLevel,
pub backend_features: Vec<String>,
@@ -389,18 +394,8 @@ fn generate_thin_lto_work<B: WriteBackendMethods>(
enum MaybeLtoModules<B: WriteBackendMethods> {
NoLto(CompiledModules),
FatLto {
cgcx: CodegenContext,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_file_for_lto: Vec<PathBuf>,
needs_fat_lto: Vec<FatLtoInput<B>>,
},
ThinLto {
cgcx: CodegenContext,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_file_for_lto: Vec<PathBuf>,
needs_thin_lto: Vec<ThinLtoInput<B>>,
},
FatLto { cgcx: CodegenContext, needs_fat_lto: Vec<FatLtoInput<B>> },
ThinLto { cgcx: CodegenContext, needs_thin_lto: Vec<ThinLtoInput<B>> },
}
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<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
allocator_module: Option<ModuleCodegen<B::Module>>,
) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel();
@@ -440,7 +434,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
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<B: WriteBackendMethods>(
prof: &SelfProfilerRef,
shared_emitter: SharedEmitter,
tm_factory: TargetMachineFactoryFn<B>,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_for_lto: Vec<PathBuf>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
needs_thin_lto: Vec<ThinLtoInput<B>>,
) -> Vec<CompiledModule> {
let _timer = prof.verbose_generic_activity("LLVM_thinlto");
@@ -1231,7 +1211,6 @@ enum MainThreadState {
fn start_executing_work<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
shared_emitter: SharedEmitter,
codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Message<B>>,
@@ -1243,22 +1222,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
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<B: ExtraBackendMethods>(
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<B: WriteBackendMethods> {
}
impl<B: WriteBackendMethods> OngoingCodegen<B> {
pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
pub fn join(
self,
sess: &Session,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
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<WorkProductId,
drop(shared_emitter);
compiled_modules
}
MaybeLtoModules::FatLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_fat_lto,
} => {
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<WorkProductId,
&cgcx,
shared_emitter,
tm_factory,
&exported_symbols_for_lto,
&each_linked_rlib_file_for_lto,
&crate_info.exported_symbols_for_lto,
&crate_info.each_linked_rlib_file_for_lto,
needs_fat_lto,
)],
allocator_module: None,
}
}
MaybeLtoModules::ThinLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
} => {
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<WorkProductId,
&sess.prof,
shared_emitter,
tm_factory,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
&crate_info.exported_symbols_for_lto,
&crate_info.each_linked_rlib_file_for_lto,
needs_thin_lto,
),
allocator_module: None,
+25 -8
View File
@@ -686,11 +686,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec<Allo
methods
}
pub fn codegen_crate<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
) -> OngoingCodegen<B> {
pub fn codegen_crate<B: ExtraBackendMethods>(backend: B, tcx: TyCtxt<'_>) -> OngoingCodegen<B> {
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<B: ExtraBackendMethods>(
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<B: ExtraBackendMethods>(
// 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
+9 -12
View File
@@ -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<DebuggerVisualizerFile>,
pub lint_levels: CodegenLintLevels,
pub metadata_symbol: String,
pub each_linked_rlib_file_for_lto: Vec<PathBuf>,
pub exported_symbols_for_lto: Vec<String>,
}
/// Target-specific options that get set in `cfg(...)`.
@@ -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())
@@ -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<dyn Any>;
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any>;
/// This is called on the returned `Box<dyn Any>` from [`codegen_crate`](Self::codegen_crate)
///
@@ -116,6 +116,7 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>);
fn print_pass_timings(&self) {}
@@ -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.
@@ -252,6 +252,8 @@ pub fn in_rvalue<'tcx, Q, F>(
in_place::<Q, _>(cx, in_local, place.as_ref())
}
Rvalue::Reborrow(_, _, place) => in_place::<Q, _>(cx, in_local, place.as_ref()),
Rvalue::WrapUnsafeBinder(op, _) => in_operand::<Q, _>(cx, in_local, op),
Rvalue::Aggregate(kind, operands) => {
@@ -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(..)
@@ -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)?;
@@ -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)?;
@@ -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
@@ -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<P: Projectable<'tcx, M::Provenance>>(
base.offset(Size::ZERO, layout, self)
}
/// Equivalent to `project_downcast`, but identifies the variant by name instead of index.
pub fn project_downcast_named<P: Projectable<'tcx, M::Provenance>>(
&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<P: Projectable<'tcx, M::Provenance>>(
&self,
@@ -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() {
@@ -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<Hcx>(&self, _: &mut Hcx) -> Self::KeyType {
self.clone()
}
}
impl<T1: ToStableHashKey, T2: ToStableHashKey> ToStableHashKey for (T1, T2) {
type KeyType = (T1::KeyType, T2::KeyType);
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&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<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
-10
View File
@@ -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<std::path::PathBuf>) -> DiagArgValue {
}
}
impl ToStableHashKey for Namespace {
type KeyType = Namespace;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> Namespace {
*self
}
}
/// Just a helper separate structure for each namespace.
#[derive(Copy, Clone, Default, Debug, StableHash)]
pub struct PerNS<T> {
+1 -1
View File
@@ -437,7 +437,7 @@ fn stable_hash<Hcx: StableHashCtxt>(&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);
+2 -54
View File
@@ -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<Hcx: StableHashCtxt>(&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<Hcx: StableHashCtxt>(&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<Hcx: StableHashCtxt>(&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<Hcx: StableHashCtxt>(&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<Hcx: StableHashCtxt>(&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
@@ -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 => (
@@ -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<FulfillmentError<'tcx>>> {
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<B>`. 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,
+18
View File
@@ -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 {
-19
View File
@@ -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<Hcx: StableHashCtxt>(&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<Hcx>(&self, _: &mut Hcx) -> ItemLocalId {
*self
}
}
+98 -1
View File
@@ -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>,
@@ -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<F>(
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))
}
@@ -343,6 +343,9 @@ pub(crate) fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment
Adjust::Pointer(_pointer_coercion) => {
// 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.
}
+110
View File
@@ -362,6 +362,116 @@ fn chunked_bitset() {
);
}
/// Additional helper methods for testing.
impl ChunkedBitSet<usize> {
/// 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<usize>, &ChunkedBitSet<usize>) -> 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<usize>, &ChunkedBitSet<usize>) -> 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<usize> {
let mut s = ChunkedBitSet::new_empty(domain_size);
for &e in elements {
+1
View File
@@ -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" }
+4 -2
View File
@@ -385,7 +385,9 @@ pub fn run_compiler<R: Send>(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<R: Send>(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| {
+3 -3
View File
@@ -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)
}
+6 -3
View File
@@ -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,
),
}
});
+1 -1
View File
@@ -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);
+12 -1
View File
@@ -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<dyn Any> {
fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
Box::new(CompiledModules { modules: vec![], allocator_module: None })
}
@@ -415,6 +417,7 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
_sess: &Session,
_outputs: &OutputFilenames,
_crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
(*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(),
+1
View File
@@ -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,
}
}
+172 -114
View File
@@ -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<extern fn()>` and `Result<extern fn(), ()>`
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,
+2 -29
View File
@@ -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<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl ToStableHashKey for LintExpectationId {
type KeyType = (DefPathHash, ItemLocalId, u16, u16);
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&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: <https://doc.rust-lang.org/rustc/lints/levels.html>
@@ -623,15 +605,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl ToStableHashKey for LintId {
type KeyType = &'static str;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> &'static str {
self.lint_name_raw()
}
}
impl StableCompare for LintId {
const CAN_USE_UNSTABLE_SORT: bool = true;
@@ -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
}
+2 -2
View File
@@ -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.
+1 -1
View File
@@ -466,7 +466,7 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
proc_macro: Table<DefIndex, MacroKind>,
deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>,
trait_impl_trait_tys: Table<DefIndex, LazyValue<DefIdMap<ty::EarlyBinder<'static, Ty<'static>>>>>,
collect_return_position_impl_trait_in_trait_tys: Table<DefIndex, LazyValue<DefIdMap<ty::EarlyBinder<'static, Ty<'static>>>>>,
doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>,
doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
+5 -6
View File
@@ -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<rustc_abi::FieldIdx, rustc_abi::VariantIdx>,
[] 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<rustc_middle::thir::Thir<'tcx>>,
[] steal_mir: rustc_data_structures::steal::Steal<rustc_middle::mir::Body<'tcx>>,
[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<rustc_hir::def_id::LocalDefId>,
[decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet<rustc_hir::ItemLocalId>,
[] is_late_bound_map: rustc_data_structures::fx::FxIndexSet<rustc_hir::ItemLocalId>,
[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<rustc_middle::ty::TyCtxt<'tcx>>,
[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,
@@ -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<Hcx>(&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;
+1 -1
View File
@@ -629,7 +629,7 @@ fn assert_dep_node_not_yet_allocated_in_current_session<S: std::fmt::Display>(
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())
+8
View File
@@ -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) => {
@@ -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<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
where
D: ?Sized + HasLocalDecls<'tcx>,
@@ -804,6 +811,7 @@ pub fn ty<D>(&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())
+19
View File
@@ -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)]
+14
View File
@@ -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));
}
}
}
+58 -2
View File
@@ -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<Ty<'a>>) -> Option<DefId> {
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<DefId> {
def_id_of_type_cached(ty, &mut SsoHashSet::new())
}
+12
View File
@@ -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.
+1
View File
@@ -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 { .. } => {}
}
}
@@ -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<U>`.
///
/// This produces an [`ExprKind::Reborrow`].
///
/// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow
GenericReborrow(hir::Mutability),
}
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
+3 -4
View File
@@ -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);
)*
}
}
@@ -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<I, T>(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<SolverLangItem> {
fn as_projection_lang_item(self, def_id: DefId) -> Option<SolverProjectionLangItem> {
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,
+1 -25
View File
@@ -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<H, T>
@@ -45,20 +44,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl<'tcx, H, T> ToStableHashKey for &'tcx ty::list::RawList<H, T>
where
T: StableHash,
{
type KeyType = Fingerprint;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&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<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
self.kind().stable_hash(hcx, hasher);
@@ -81,12 +66,3 @@ fn stable_hash<Hcx: StableHashCtxt>(&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<Hcx>(&self, _: &mut Hcx) -> region::Scope {
*self
}
}
@@ -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!();
}
}
}
@@ -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()))
}
}
}
@@ -43,7 +43,8 @@ pub(crate) fn of(ek: &ExprKind<'_>) -> Option<Category> {
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. }
| ExprKind::PlaceUnwrapUnsafeBinder { .. }
| ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place),
| ExprKind::ValueUnwrapUnsafeBinder { .. }
| ExprKind::Reborrow { .. } => Some(Category::Place),
ExprKind::LogicalOp { .. }
| ExprKind::Match { .. }
@@ -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 {
@@ -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.
@@ -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 }
@@ -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")
}
}
}
@@ -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);
}
}
}
@@ -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);
}
@@ -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(..) => {}
}
}
@@ -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() {
@@ -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;
}
+15
View File
@@ -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));
@@ -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)?;
+1
View File
@@ -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,
};
@@ -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)?;
+1 -1
View File
@@ -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);
@@ -69,7 +69,7 @@ fn instantiate_canonical_var(
fn add_item_bounds_for_hidden_type(
&self,
def_id: <Self::Interner as Interner>::DefId,
def_id: <Self::Interner as Interner>::OpaqueTyId,
args: <Self::Interner as Interner>::GenericArgs,
param_env: <Self::Interner as Interner>::ParamEnv,
hidden_ty: <Self::Interner as Interner>::Ty,
@@ -79,7 +79,7 @@ fn add_item_bounds_for_hidden_type(
fn fetch_eligible_assoc_item(
&self,
goal_trait_ref: ty::TraitRef<Self::Interner>,
trait_assoc_def_id: <Self::Interner as Interner>::DefId,
trait_assoc_def_id: <Self::Interner as Interner>::TraitAssocTermId,
impl_def_id: <Self::Interner as Interner>::ImplId,
) -> FetchEligibleAssocItemResponse<Self::Interner>;
@@ -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<D, I>(
// 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<I:
);
}
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 {
@@ -596,7 +599,8 @@ fn fn_item_to_async_callable<I: Interner>(
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<I: Interner>(
args: ty::CoroutineClosureArgs<I>,
sig: ty::CoroutineClosureSignature<I>,
) -> 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<D, I>(
// 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));
}
}
@@ -702,7 +702,7 @@ fn should_rerun_after_erased_canonicalization(
original_typing_mode: TypingMode<I>,
parent_opaque_types: &[(OpaqueTypeKey<I>, 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<I>,
trait_assoc_def_id: I::DefId,
trait_assoc_def_id: I::TraitAssocTermId,
impl_def_id: I::ImplId,
) -> FetchEligibleAssocItemResponse<I> {
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,
@@ -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()))
}
}
}
@@ -13,6 +13,7 @@ impl<D, I> EvalCtxt<'_, D>
pub(super) fn normalize_anon_const(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
def_id: I::UnevaluatedConstId,
) -> QueryResult<I> {
let uv = goal.predicate.alias.expect_ct(self.cx());
self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv)
@@ -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,
@@ -18,18 +18,19 @@ impl<D, I> EvalCtxt<'_, D>
pub(super) fn normalize_inherent_associated_term(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
def_id: I::InherentAssocTermId,
) -> QueryResult<I> {
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
@@ -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<I, NormalizesTo<I>>,
) -> QueryResult<I> {
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<I, ty::TraitPredicate<I>> = 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<I, NormalizesTo<I>>) -> QueryResult<I> {
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<I, ty::TraitPredicate<I>> = 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 `<Vec<u32> as Trait<i32>>::Assoc<u64>`
@@ -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<Candidate<I>, 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<I, Self>,
) -> Result<Candidate<I>, 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::<I::DefId>::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| {
@@ -17,6 +17,7 @@ impl<D, I> EvalCtxt<'_, D>
pub(super) fn normalize_opaque_type(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
def_id: I::OpaqueTyId,
) -> QueryResult<I> {
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)
}
@@ -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())
+8
View File
@@ -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<Ty, Error> {
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()))

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