mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-21 17:52:12 +03:00
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:
+1
-1
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
+1
-1
@@ -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",
|
||||
);
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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", || {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)>>,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user