Auto merge of #156278 - JonathanBrouwer:rollup-O8O1IcI, r=JonathanBrouwer

Rollup of 10 pull requests

Successful merges:

 - rust-lang/rust#146273 (lint ImproperCTypes: refactor linting architecture (part 2))
 - rust-lang/rust#154025 (Add `keepalive`, `set_keepalive` to `TcpStream` implementations)
 - rust-lang/rust#156024 (CFI: Fix LTO for `#![no_builtins]` crates with CFI)
 - rust-lang/rust#156243 (Move CrateInfo computation after codegen_crate)
 - rust-lang/rust#154846 (Add better default spans for the `Ty` impl of `QueryKey`)
 - rust-lang/rust#155220 (cg_clif: Don't show verbose run-make cmd output for passing tests)
 - rust-lang/rust#156204 (Implemented `PathBuf::into_string`)
 - rust-lang/rust#156245 (Move invocation_temp into OutputFilenames)
 - rust-lang/rust#156250 (add a few new solver normalization tests)
 - rust-lang/rust#156265 (Remove unused `ToStableHashKey` impls.)
This commit is contained in:
bors
2026-05-07 14:59:29 +00:00
142 changed files with 1908 additions and 654 deletions
+1 -1
View File
@@ -4101,6 +4101,7 @@ dependencies = [
name = "rustc_interface"
version = "0.0.0"
dependencies = [
"rand 0.9.2",
"rustc_abi",
"rustc_ast",
"rustc_ast_lowering",
@@ -4602,7 +4603,6 @@ version = "0.0.0"
dependencies = [
"getopts",
"libc",
"rand 0.9.2",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
@@ -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
@@ -150,7 +150,6 @@ fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
fn emit_cgu(
output_filenames: &OutputFilenames,
invocation_temp: Option<&str>,
prof: &SelfProfilerRef,
name: String,
module: UnwindModule<ObjectModule>,
@@ -166,7 +165,6 @@ fn emit_cgu(
let module_regular = emit_module(
output_filenames,
invocation_temp,
prof,
product.object,
ModuleKind::Regular,
@@ -192,7 +190,6 @@ fn emit_cgu(
fn emit_module(
output_filenames: &OutputFilenames,
invocation_temp: Option<&str>,
prof: &SelfProfilerRef,
mut object: cranelift_object::object::write::Object<'_>,
kind: ModuleKind,
@@ -211,7 +208,7 @@ fn emit_module(
object.set_section_data(comment_section, producer, 1);
}
let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp);
let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name);
let file = match File::create(&tmp_file) {
Ok(file) => file,
Err(err) => return Err(format!("error creating object file: {}", err)),
@@ -251,11 +248,8 @@ fn reuse_workproduct_for_cgu(
cgu: &CodegenUnit<'_>,
) -> Result<ModuleCodegenResult, String> {
let work_product = cgu.previous_work_product(tcx);
let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu(
OutputType::Object,
cgu.name().as_str(),
tcx.sess.invocation_temp.as_deref(),
);
let obj_out_regular =
tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str());
let source_file_regular = rustc_incremental::in_incr_comp_dir_sess(
tcx.sess,
work_product.saved_files.get("o").expect("no saved object file in work product"),
@@ -394,7 +388,6 @@ fn module_codegen(
let producer = crate::debuginfo::producer(tcx.sess);
let profiler = tcx.prof.clone();
let invocation_temp = tcx.sess.invocation_temp.clone();
let output_filenames = tcx.output_filenames(()).clone();
let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess);
@@ -421,19 +414,13 @@ fn module_codegen(
let global_asm_object_file =
profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {
crate::global_asm::compile_global_asm(
&global_asm_config,
&cgu_name,
global_asm,
invocation_temp.as_deref(),
)
crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm)
})?;
let codegen_result =
profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {
emit_cgu(
&global_asm_config.output_filenames,
invocation_temp.as_deref(),
&profiler,
cgu_name,
module,
@@ -456,7 +443,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> {
match emit_module(
tcx.output_filenames(()),
tcx.sess.invocation_temp.as_deref(),
&tcx.sess.prof,
product.object,
ModuleKind::Allocator,
@@ -33,14 +33,15 @@ fn create_jit_module(
(jit_module, cx)
}
pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec<String>) -> ! {
pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, jit_args: Vec<String>) -> ! {
if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) {
tcx.dcx().fatal("can't jit non-executable crate");
}
let output_filenames = tcx.output_filenames(());
let crate_info = CrateInfo::new(tcx, target_cpu);
let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess);
let (mut jit_module, mut debug_context) = create_jit_module(tcx, crate_info);
let (mut jit_module, mut debug_context) = create_jit_module(tcx, &crate_info);
let mut cached_context = Context::new();
let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;
@@ -185,7 +185,6 @@ pub(crate) fn compile_global_asm(
config: &GlobalAsmConfig,
cgu_name: &str,
global_asm: String,
invocation_temp: Option<&str>,
) -> Result<Option<PathBuf>, String> {
if global_asm.is_empty() {
return Ok(None);
@@ -200,7 +199,7 @@ pub(crate) fn compile_global_asm(
global_asm.push('\n');
let global_asm_object_file = add_file_stem_postfix(
config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp),
config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name),
".asm",
);
+3 -2
View File
@@ -209,12 +209,12 @@ fn target_cpu(&self, sess: &Session) -> String {
.to_owned()
}
fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box<dyn Any> {
fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE));
let config = self.config.get().unwrap();
if config.jit_mode {
#[cfg(feature = "jit")]
driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone());
driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone());
#[cfg(not(feature = "jit"))]
tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift");
@@ -228,6 +228,7 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
_crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs)
}
+5 -21
View File
@@ -29,16 +29,8 @@ pub(crate) fn codegen(
let lto_mode = module.module_llvm.lto_mode;
let lto_supported = module.module_llvm.lto_supported;
let bc_out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::Bitcode,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let obj_out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::Object,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name);
let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name);
if config.bitcode_needed() {
let _timer =
@@ -82,22 +74,15 @@ pub(crate) fn codegen(
}
if config.emit_ir {
let out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::LlvmAssembly,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let out =
cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name);
std::fs::write(out, "").expect("write file");
}
if config.emit_asm {
let _timer =
prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
let path = cgcx.output_filenames.temp_path_for_cgu(
OutputType::Assembly,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name);
context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
}
@@ -215,7 +200,6 @@ pub(crate) fn codegen(
config.emit_asm,
config.emit_ir,
&cgcx.output_filenames,
cgcx.invocation_temp.as_deref(),
)
}
+4 -3
View File
@@ -291,8 +291,8 @@ fn target_cpu(&self, sess: &Session) -> String {
target_cpu(sess).to_owned()
}
fn codegen_crate(&self, tcx: TyCtxt<'_>, crate_info: &CrateInfo) -> Box<dyn Any> {
Box::new(codegen_crate(self.clone(), tcx, crate_info))
fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
Box::new(codegen_crate(self.clone(), tcx))
}
fn join_codegen(
@@ -300,11 +300,12 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
sess: &Session,
_outputs: &OutputFilenames,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
.expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
.join(sess)
.join(sess, crate_info)
}
fn target_config(&self, sess: &Session) -> TargetConfig {
+11 -40
View File
@@ -117,17 +117,13 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar
tcx.sess.split_debuginfo(),
tcx.sess.opts.unstable_opts.split_dwarf_kind,
mod_name,
tcx.sess.invocation_temp.as_deref(),
)
} else {
None
};
let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu(
OutputType::Object,
mod_name,
tcx.sess.invocation_temp.as_deref(),
));
let output_obj_file =
Some(tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, mod_name));
let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file };
target_machine_factory(
@@ -322,11 +318,7 @@ pub(crate) fn save_temp_bitcode(
return;
}
let ext = format!("{name}.bc");
let path = cgcx.output_filenames.temp_path_ext_for_cgu(
&ext,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let path = cgcx.output_filenames.temp_path_ext_for_cgu(&ext, &module.name);
write_bitcode_to_file(&module.module_llvm, &path)
}
@@ -949,11 +941,8 @@ pub(crate) fn optimize(
if let Some(thin_lto_buffer) = thin_lto_buffer {
let thin_lto_buffer = thin_lto_buffer.unwrap();
module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec());
let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::ThinLinkBitcode,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let bc_summary_out =
cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name);
if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer
&& let Some(thin_link_bitcode_filename) = bc_summary_out.file_name()
{
@@ -1008,16 +997,8 @@ pub(crate) fn codegen(
// copy it to the .o file, and delete the bitcode if it wasn't
// otherwise requested.
let bc_out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::Bitcode,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let obj_out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::Object,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name);
let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name);
if config.bitcode_needed() {
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
@@ -1055,11 +1036,8 @@ pub(crate) fn codegen(
if config.emit_ir {
let _timer =
prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
let out = cgcx.output_filenames.temp_path_for_cgu(
OutputType::LlvmAssembly,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let out =
cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name);
let out_c = path_to_c_string(&out);
extern "C" fn demangle_callback(
@@ -1103,11 +1081,7 @@ extern "C" fn demangle_callback(
if config.emit_asm {
let _timer =
prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
let path = cgcx.output_filenames.temp_path_for_cgu(
OutputType::Assembly,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name);
// We can't use the same module for asm and object code output,
// because that triggers various errors like invalid IR or broken
@@ -1136,9 +1110,7 @@ extern "C" fn demangle_callback(
let _timer =
prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
let dwo_out = cgcx
.output_filenames
.temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref());
let dwo_out = cgcx.output_filenames.temp_path_dwo_for_cgu(&module.name);
let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) {
// Don't change how DWARF is emitted when disabled.
(SplitDebuginfo::Off, _) => None,
@@ -1203,7 +1175,6 @@ extern "C" fn demangle_callback(
config.emit_asm,
config.emit_ir,
&cgcx.output_filenames,
cgcx.invocation_temp.as_deref(),
)
}
@@ -903,7 +903,6 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
tcx.sess.split_debuginfo(),
tcx.sess.opts.unstable_opts.split_dwarf_kind,
codegen_unit_name,
tcx.sess.invocation_temp.as_deref(),
) {
// We get a path relative to the working directory from split_dwarf_path
Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f))
+4 -3
View File
@@ -333,8 +333,8 @@ fn target_cpu(&self, sess: &Session) -> String {
crate::llvm_util::target_cpu(sess).to_string()
}
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box<dyn Any> {
Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info))
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx))
}
fn join_codegen(
@@ -342,11 +342,12 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
let (compiled_modules, work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
.join(sess);
.join(sess, crate_info);
if sess.opts.unstable_opts.llvm_time_trace {
sess.time("llvm_dump_timing_file", || {
+1 -6
View File
@@ -112,12 +112,7 @@ pub fn link_binary(
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
let crate_name = format!("{}", crate_info.local_crate_name);
let out_filename = output.file_for_writing(
outputs,
OutputType::Exe,
&crate_name,
sess.invocation_temp.as_deref(),
);
let out_filename = output.file_for_writing(outputs, OutputType::Exe, &crate_name);
match crate_type {
CrateType::Rlib => {
let _timer = sess.timer("link_rlib");
+1 -1
View File
@@ -85,7 +85,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool {
}
}
pub(super) fn exported_symbols_for_lto(
pub(crate) fn exported_symbols_for_lto(
tcx: TyCtxt<'_>,
each_linked_rlib_for_lto: &[CrateNum],
) -> Vec<String> {
+46 -97
View File
@@ -34,7 +34,7 @@
use rustc_target::spec::{MergeFunctions, SanitizerSet};
use tracing::debug;
use crate::back::link::{self, ensure_removed};
use crate::back::link::ensure_removed;
use crate::back::lto::{self, SerializedModule, check_lto_allowed};
use crate::errors::ErrorCreatingRemarkDir;
use crate::traits::*;
@@ -136,7 +136,8 @@ macro_rules! if_regular {
let emit_obj = if !should_emit_obj {
EmitObj::None
} else if sess.target.obj_is_bitcode
|| (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins)
|| (sess.opts.cg.linker_plugin_lto.enabled()
&& (!no_builtins || tcx.sess.is_sanitizer_cfi_enabled()))
{
// This case is selected if the target uses objects as bitcode, or
// if linker plugin LTO is enabled. In the linker plugin LTO case
@@ -144,14 +145,23 @@ macro_rules! if_regular {
// and convert it to object code. This may be done by either the
// native linker or rustc itself.
//
// Note, however, that the linker-plugin-lto requested here is
// explicitly ignored for `#![no_builtins]` crates. These crates are
// specifically ignored by rustc's LTO passes and wouldn't work if
// loaded into the linker. These crates define symbols that LLVM
// lowers intrinsics to, and these symbol dependencies aren't known
// until after codegen. As a result any crate marked
// `#![no_builtins]` is assumed to not participate in LTO and
// instead goes on to generate object code.
// By default this branch is skipped for `#![no_builtins]` crates so
// they emit native object files (machine code), not LLVM bitcode
// objects for the linker (see rust-lang/rust#146133).
//
// However, when LLVM CFI is enabled (`-Zsanitizer=cfi`), this
// breaks LLVM's expected pipeline: LLVM emits `llvm.type.test`
// intrinsics and related metadata that must be lowered by LLVM's
// `LowerTypeTests` pass before instruction selection during
// link-time LTO. Otherwise, `llvm.type.test` intrinsics and related
// metadata are not lowered by LLVM's `LowerTypeTests` pass before
// reaching the target backend, and LLVM may abort during codegen
// (for example in SelectionDAG type legalization) (see
// rust-lang/rust#142284).
//
// Therefore, with `-Clinker-plugin-lto` and `-Zsanitizer=cfi`, a
// `#![no_builtins]` crate must still use rustc's `EmitObj::Bitcode`
// path (and emit LLVM bitcode in the `.o` for linker-based LTO).
EmitObj::Bitcode
} else if need_bitcode_in_object(tcx) || sess.target.requires_lto {
EmitObj::ObjectCode(BitcodeSection::Full)
@@ -285,17 +295,13 @@ pub fn new(cgcx: &CodegenContext, module_name: &str) -> TargetMachineFactoryConf
cgcx.split_debuginfo,
cgcx.split_dwarf_kind,
module_name,
cgcx.invocation_temp.as_deref(),
)
} else {
None
};
let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu(
OutputType::Object,
module_name,
cgcx.invocation_temp.as_deref(),
));
let output_obj_file =
Some(cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, module_name));
TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }
}
}
@@ -322,7 +328,6 @@ pub struct CodegenContext {
pub time_trace: bool,
pub crate_types: Vec<CrateType>,
pub output_filenames: Arc<OutputFilenames>,
pub invocation_temp: Option<String>,
pub module_config: Arc<ModuleConfig>,
pub opt_level: OptLevel,
pub backend_features: Vec<String>,
@@ -389,18 +394,8 @@ fn generate_thin_lto_work<B: WriteBackendMethods>(
enum MaybeLtoModules<B: WriteBackendMethods> {
NoLto(CompiledModules),
FatLto {
cgcx: CodegenContext,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_file_for_lto: Vec<PathBuf>,
needs_fat_lto: Vec<FatLtoInput<B>>,
},
ThinLto {
cgcx: CodegenContext,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_file_for_lto: Vec<PathBuf>,
needs_thin_lto: Vec<ThinLtoInput<B>>,
},
FatLto { cgcx: CodegenContext, needs_fat_lto: Vec<FatLtoInput<B>> },
ThinLto { cgcx: CodegenContext, needs_thin_lto: Vec<ThinLtoInput<B>> },
}
fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool {
@@ -424,7 +419,6 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool {
pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
allocator_module: Option<ModuleCodegen<B::Module>>,
) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel();
@@ -440,7 +434,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
let coordinator_thread = start_executing_work(
backend.clone(),
tcx,
crate_info,
shared_emitter,
codegen_worker_send,
coordinator_receive,
@@ -529,11 +522,7 @@ pub fn produce_final_output_artifacts(
if let [module] = &compiled_modules.modules[..] {
// 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
let path = crate_output.temp_path_for_cgu(
output_type,
&module.name,
sess.invocation_temp.as_deref(),
);
let path = crate_output.temp_path_for_cgu(output_type, &module.name);
let output = crate_output.path(output_type);
if !output_type.is_text_output() && output.is_tty() {
sess.dcx()
@@ -912,12 +901,7 @@ fn execute_copy_from_cache_work_item(
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
let dwarf_obj_out = cgcx
.output_filenames
.split_dwarf_path(
cgcx.split_debuginfo,
cgcx.split_dwarf_kind,
&module.name,
cgcx.invocation_temp.as_deref(),
)
.split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, &module.name)
.expect(
"saved dwarf object in work product but `split_dwarf_path` returned `None`",
);
@@ -927,11 +911,7 @@ fn execute_copy_from_cache_work_item(
let mut load_from_incr_cache = |perform, output_type: OutputType| {
if perform {
let saved_file = module.source.saved_files.get(output_type.extension())?;
let output_path = cgcx.output_filenames.temp_path_for_cgu(
output_type,
&module.name,
cgcx.invocation_temp.as_deref(),
);
let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name);
load_from_incr_comp_dir(output_path, &saved_file)
} else {
None
@@ -992,8 +972,8 @@ fn do_thin_lto<B: WriteBackendMethods>(
prof: &SelfProfilerRef,
shared_emitter: SharedEmitter,
tm_factory: TargetMachineFactoryFn<B>,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_for_lto: Vec<PathBuf>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
needs_thin_lto: Vec<ThinLtoInput<B>>,
) -> Vec<CompiledModule> {
let _timer = prof.verbose_generic_activity("LLVM_thinlto");
@@ -1231,7 +1211,6 @@ enum MainThreadState {
fn start_executing_work<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
shared_emitter: SharedEmitter,
codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Message<B>>,
@@ -1243,22 +1222,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
let sess = tcx.sess;
let prof = sess.prof.clone();
let mut each_linked_rlib_for_lto = Vec::new();
let mut each_linked_rlib_file_for_lto = Vec::new();
if sess.lto() != Lto::No && sess.lto() != Lto::ThinLocal {
drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
if link::ignored_for_lto(sess, crate_info, cnum) {
return;
}
each_linked_rlib_for_lto.push(cnum);
each_linked_rlib_file_for_lto.push(path.to_path_buf());
}));
}
// Compute the set of symbols we need to retain when doing LTO (if we need to)
// Compute the set of symbols we need to retain when doing thin local LTO (if we need to)
let exported_symbols_for_lto =
Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto));
if sess.lto() == Lto::ThinLocal { lto::exported_symbols_for_lto(tcx, &[]) } else { vec![] };
// First up, convert our jobserver into a helper thread so we can use normal
// mpsc channels to manage our messages and such.
@@ -1313,7 +1279,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend,
pointer_size: tcx.data_layout.pointer_size(),
invocation_temp: sess.invocation_temp.clone(),
};
// This is the "main loop" of parallel work happening for parallel codegen.
@@ -1757,12 +1722,7 @@ enum CodegenState {
needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, bitcode_path })
}
return Ok(MaybeLtoModules::FatLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_fat_lto,
});
return Ok(MaybeLtoModules::FatLto { cgcx, needs_fat_lto });
} else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() {
assert!(compiled_modules.is_empty());
assert!(needs_fat_lto.is_empty());
@@ -1777,8 +1737,8 @@ enum CodegenState {
&prof,
shared_emitter.clone(),
tm_factory,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
&exported_symbols_for_lto,
&[],
needs_thin_lto,
));
} else {
@@ -1790,12 +1750,7 @@ enum CodegenState {
});
}
return Ok(MaybeLtoModules::ThinLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
});
return Ok(MaybeLtoModules::ThinLto { cgcx, needs_thin_lto });
}
}
@@ -2139,7 +2094,11 @@ pub struct OngoingCodegen<B: WriteBackendMethods> {
}
impl<B: WriteBackendMethods> OngoingCodegen<B> {
pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
pub fn join(
self,
sess: &Session,
crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
self.shared_emitter_main.check(sess, true);
let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() {
@@ -2163,12 +2122,7 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId,
drop(shared_emitter);
compiled_modules
}
MaybeLtoModules::FatLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_fat_lto,
} => {
MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => {
let tm_factory = self.backend.target_machine_factory(
sess,
cgcx.opt_level,
@@ -2181,19 +2135,14 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId,
&cgcx,
shared_emitter,
tm_factory,
&exported_symbols_for_lto,
&each_linked_rlib_file_for_lto,
&crate_info.exported_symbols_for_lto,
&crate_info.each_linked_rlib_file_for_lto,
needs_fat_lto,
)],
allocator_module: None,
}
}
MaybeLtoModules::ThinLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
} => {
MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => {
let tm_factory = self.backend.target_machine_factory(
sess,
cgcx.opt_level,
@@ -2206,8 +2155,8 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId,
&sess.prof,
shared_emitter,
tm_factory,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
&crate_info.exported_symbols_for_lto,
&crate_info.each_linked_rlib_file_for_lto,
needs_thin_lto,
),
allocator_module: None,
+23 -6
View File
@@ -686,11 +686,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec<Allo
methods
}
pub fn codegen_crate<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
) -> OngoingCodegen<B> {
pub fn codegen_crate<B: ExtraBackendMethods>(backend: B, tcx: TyCtxt<'_>) -> OngoingCodegen<B> {
if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() {
// The target has no default cpu, but none is set explicitly
tcx.dcx().emit_fatal(errors::CpuRequired);
@@ -734,7 +730,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
None
};
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, crate_info, allocator_module);
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module);
// For better throughput during parallel processing by LLVM, we used to sort
// CGUs largest to smallest. This would lead to better thread utilization
@@ -959,6 +955,8 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
natvis_debugger_visualizers: Default::default(),
lint_levels: CodegenLintLevels::from_tcx(tcx),
metadata_symbol: exported_symbols::metadata_symbol_name(tcx),
each_linked_rlib_file_for_lto: Default::default(),
exported_symbols_for_lto: Default::default(),
};
info.native_libraries.reserve(n_crates);
@@ -1044,6 +1042,25 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
});
}
let mut each_linked_rlib_for_lto = Vec::new();
let mut each_linked_rlib_file_for_lto = Vec::new();
if tcx.sess.lto() != config::Lto::No && tcx.sess.lto() != config::Lto::ThinLocal {
drop(crate::back::link::each_linked_rlib(&info, None, &mut |cnum, path| {
if crate::back::link::ignored_for_lto(tcx.sess, &info, cnum) {
return;
}
each_linked_rlib_for_lto.push(cnum);
each_linked_rlib_file_for_lto.push(path.to_path_buf());
}));
}
info.each_linked_rlib_file_for_lto = each_linked_rlib_file_for_lto;
// FIXME move to -Zlink-only half such that each_linked_rlib_file_for_lto can be moved there too
// Compute the set of symbols we need to retain when doing LTO (if we need to)
info.exported_symbols_for_lto =
crate::back::lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto);
let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type {
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => {
// These are crate types for which we invoke the linker and can embed
+9 -12
View File
@@ -95,19 +95,14 @@ pub fn into_compiled_module(
emit_asm: bool,
emit_ir: bool,
outputs: &OutputFilenames,
invocation_temp: Option<&str>,
) -> CompiledModule {
let object = emit_obj
.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp));
let dwarf_object =
emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp));
let bytecode = emit_bc
.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp));
let assembly = emit_asm
.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp));
let llvm_ir = emit_ir.then(|| {
outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp)
});
let object = emit_obj.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name));
let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name));
let bytecode = emit_bc.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name));
let assembly =
emit_asm.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name));
let llvm_ir =
emit_ir.then(|| outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name));
CompiledModule {
name: self.name,
@@ -230,6 +225,8 @@ pub struct CrateInfo {
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
pub lint_levels: CodegenLintLevels,
pub metadata_symbol: String,
pub each_linked_rlib_file_for_lto: Vec<PathBuf>,
pub exported_symbols_for_lto: Vec<String>,
}
/// Target-specific options that get set in `cfg(...)`.
@@ -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) {}
@@ -459,22 +459,6 @@ impl StableOrd for String {
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl ToStableHashKey for String {
type KeyType = String;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> Self::KeyType {
self.clone()
}
}
impl<T1: ToStableHashKey, T2: ToStableHashKey> ToStableHashKey for (T1, T2) {
type KeyType = (T1::KeyType, T2::KeyType);
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> Self::KeyType {
(self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx))
}
}
impl StableHash for bool {
#[inline]
fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
-10
View File
@@ -4,7 +4,6 @@
use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_data_structures::stable_hasher::ToStableHashKey;
use rustc_data_structures::unord::UnordMap;
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_macros::{Decodable, Encodable, StableHash};
@@ -712,15 +711,6 @@ fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
}
}
impl ToStableHashKey for Namespace {
type KeyType = Namespace;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> Namespace {
*self
}
}
/// Just a helper separate structure for each namespace.
#[derive(Copy, Clone, Default, Debug, StableHash)]
pub struct PerNS<T> {
+2 -54
View File
@@ -1,59 +1,7 @@
use rustc_data_structures::stable_hasher::{
StableHash, StableHashCtxt, StableHasher, ToStableHashKey,
};
use rustc_span::def_id::DefPathHash;
use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher};
use crate::HashIgnoredAttrId;
use crate::hir::{
AttributeMap, BodyId, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
};
use crate::hir_id::ItemLocalId;
impl ToStableHashKey for BodyId {
type KeyType = (DefPathHash, ItemLocalId);
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) {
let BodyId { hir_id } = *self;
hir_id.to_stable_hash_key(hcx)
}
}
impl ToStableHashKey for ItemId {
type KeyType = DefPathHash;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> DefPathHash {
self.owner_id.def_id.to_stable_hash_key(hcx)
}
}
impl ToStableHashKey for TraitItemId {
type KeyType = DefPathHash;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> DefPathHash {
self.owner_id.def_id.to_stable_hash_key(hcx)
}
}
impl ToStableHashKey for ImplItemId {
type KeyType = DefPathHash;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> DefPathHash {
self.owner_id.def_id.to_stable_hash_key(hcx)
}
}
impl ToStableHashKey for ForeignItemId {
type KeyType = DefPathHash;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> DefPathHash {
self.owner_id.def_id.to_stable_hash_key(hcx)
}
}
use crate::hir::{AttributeMap, OwnerNodes};
// The following implementations of StableHash for `ItemId`, `TraitItemId`, and
// `ImplItemId` deserve special attention. Normally we do not hash `NodeId`s within
-19
View File
@@ -176,22 +176,3 @@ impl StableOrd for ItemLocalId {
HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO };
pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID };
impl ToStableHashKey for HirId {
type KeyType = (DefPathHash, ItemLocalId);
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) {
let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx);
(def_path_hash, self.local_id)
}
}
impl ToStableHashKey for ItemLocalId {
type KeyType = ItemLocalId;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> ItemLocalId {
*self
}
}
+1
View File
@@ -5,6 +5,7 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
rand = "0.9.0"
rustc_ast = { path = "../rustc_ast" }
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
rustc_ast_passes = { path = "../rustc_ast_passes" }
+3 -3
View File
@@ -1305,8 +1305,6 @@ pub(crate) fn start_codegen<'tcx>(
let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx);
let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess));
let codegen = tcx.sess.time("codegen_crate", || {
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
// Skip crate items and just output metadata in -Z no-codegen mode.
@@ -1315,7 +1313,7 @@ pub(crate) fn start_codegen<'tcx>(
// Linker::link will skip join_codegen in case of a CodegenResults Any value.
Box::new(CompiledModules { modules: vec![], allocator_module: None })
} else {
codegen_backend.codegen_crate(tcx, &crate_info)
codegen_backend.codegen_crate(tcx)
}
});
@@ -1327,6 +1325,8 @@ pub(crate) fn start_codegen<'tcx>(
tcx.sess.code_stats.print_type_sizes();
}
let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess));
(codegen, crate_info, metadata)
}
+6 -3
View File
@@ -53,9 +53,12 @@ pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) {
// This was a check only build
Ok(compiled_modules) => (*compiled_modules, IndexMap::default()),
Err(ongoing_codegen) => {
codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames)
}
Err(ongoing_codegen) => codegen_backend.join_codegen(
ongoing_codegen,
sess,
&self.output_filenames,
&self.crate_info,
),
}
});
+12 -1
View File
@@ -5,6 +5,7 @@
use std::sync::{Arc, OnceLock};
use std::{env, thread};
use rand::{RngCore, rng};
use rustc_ast as ast;
use rustc_attr_parsing::ShouldEmit;
use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder};
@@ -12,6 +13,7 @@
use rustc_codegen_ssa::target_features::cfg_target_feature;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig};
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::jobserver::Proxy;
use rustc_data_structures::sync;
@@ -406,7 +408,7 @@ fn target_cpu(&self, _sess: &Session) -> String {
String::new()
}
fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>, _crate_info: &CrateInfo) -> Box<dyn Any> {
fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
Box::new(CompiledModules { modules: vec![], allocator_module: None })
}
@@ -415,6 +417,7 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
_sess: &Session,
_outputs: &OutputFilenames,
_crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
(*ongoing_codegen.downcast().unwrap(), FxIndexMap::default())
}
@@ -615,6 +618,12 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string())
});
let invocation_temp = sess
.opts
.incremental
.as_ref()
.map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string());
match sess.io.output_file {
None => {
// "-" as input file will cause the parser to read from stdin so we
@@ -631,6 +640,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
stem,
None,
sess.io.temps_dir.clone(),
invocation_temp,
sess.opts.unstable_opts.split_dwarf_out_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
@@ -661,6 +671,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
out_filestem,
ofile,
sess.io.temps_dir.clone(),
invocation_temp,
sess.opts.unstable_opts.split_dwarf_out_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
+172 -114
View File
@@ -138,17 +138,16 @@
USES_POWER_ALIGNMENT
]);
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
#[inline]
fn get_type_from_field<'tcx>(
/// A common pattern in this lint is to attempt normalize_erasing_regions,
/// but keep the original type if it were to fail.
/// This may or may not be supported in the logic behind the `Unnormalized` wrapper,
/// (FIXME?)
/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be.
fn maybe_normalize_erasing_regions<'tcx>(
cx: &LateContext<'tcx>,
field: &ty::FieldDef,
args: GenericArgsRef<'tcx>,
value: Unnormalized<'tcx, Ty<'tcx>>,
) -> Ty<'tcx> {
let field_ty = field.ty(cx.tcx, args);
cx.tcx
.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty))
.unwrap_or(field_ty)
cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip())
}
/// Check a variant of a non-exhaustive enum for improper ctypes
@@ -257,12 +256,22 @@ fn check_struct_for_power_alignment<'tcx>(
}
}
/// Annotates whether we are in the context of an item *defined* in rust
/// and exposed to an FFI boundary,
/// or the context of an item from elsewhere, whose interface is re-*declared* in rust.
#[derive(Clone, Copy)]
enum CItemKind {
Declaration,
Definition,
}
/// Annotates whether we are in the context of a function's argument types or return type.
#[derive(Clone, Copy)]
enum FnPos {
Arg,
Ret,
}
enum FfiResult<'tcx> {
FfiSafe,
FfiPhantom(Ty<'tcx>),
@@ -286,8 +295,10 @@ enum IndirectionKind {
}
bitflags! {
/// VisitorState flags that are linked with the root type's use.
/// (These are the permanent part of the state, kept when visiting new Ty.)
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct VisitorState: u8 {
struct RootUseFlags: u8 {
/// For use in (externally-linked) static variables.
const STATIC = 0b000001;
/// For use in functions in general.
@@ -302,7 +313,45 @@ struct VisitorState: u8 {
}
}
impl VisitorState {
/// Description of the relationship between current Ty and
/// the type (or lack thereof) immediately containing it
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum OuterTyKind {
None,
/// A variant that should not exist,
/// but is needed because we don't change the lint's behavior yet
NoneThroughFnPtr,
/// Placeholder for properties that will be used eventually
Other,
}
impl OuterTyKind {
/// Computes the relationship by providing the containing Ty itself
fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self {
match ty.kind() {
ty::FnPtr(..) => Self::NoneThroughFnPtr,
ty::RawPtr(..)
| ty::Ref(..)
| ty::Adt(..)
| ty::Tuple(..)
| ty::Array(..)
| ty::Slice(_) => OuterTyKind::Other,
_ => bug!("Unexpected outer type {ty:?}"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct VisitorState {
/// Flags describing both the overall context in which the current Ty is,
/// linked to how the Visitor's original Ty was used.
root_use_flags: RootUseFlags,
/// Flags describing both the immediate context in which the current Ty is,
/// linked to how it relates to its parent Ty (or lack thereof).
outer_ty_kind: OuterTyKind,
}
impl RootUseFlags {
// The values that can be set.
const STATIC_TY: Self = Self::STATIC;
const ARGUMENT_TY_IN_DEFINITION: Self =
@@ -317,86 +366,85 @@ impl VisitorState {
const RETURN_TY_IN_FNPTR: Self =
Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits())
.unwrap();
}
/// Get the proper visitor state for a given function's arguments.
fn argument_from_fnmode(fn_mode: CItemKind) -> Self {
match fn_mode {
CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION,
CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION,
impl VisitorState {
/// From an existing state, compute the state of any subtype of the current type.
/// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.)
fn next(&self, current_ty: Ty<'_>) -> Self {
assert!(!matches!(current_ty.kind(), ty::FnPtr(..)));
VisitorState {
root_use_flags: self.root_use_flags,
outer_ty_kind: OuterTyKind::from_ty(current_ty),
}
}
/// Get the proper visitor state for a given function's return type.
fn return_from_fnmode(fn_mode: CItemKind) -> Self {
match fn_mode {
CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION,
CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION,
/// From an existing state, compute the state of any subtype of the current type.
/// (Case where the current type is a function pointer,
/// meaning we need to specify if the subtype is an argument or the return.)
fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self {
assert!(matches!(current_ty.kind(), ty::FnPtr(..)));
VisitorState {
root_use_flags: match fn_pos {
FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR,
FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR,
},
outer_ty_kind: OuterTyKind::from_ty(current_ty),
}
}
/// Get the proper visitor state for a given function's arguments or return type.
fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self {
let p_flags = match (fn_mode, fn_pos) {
(CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION,
(CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION,
(CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION,
(CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION,
};
VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None }
}
/// Get the proper visitor state for a static variable's type
fn static_entry_point() -> Self {
VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None }
}
/// Whether the type is used in a function.
fn is_in_function(self) -> bool {
let ret = self.contains(Self::FUNC);
fn is_in_function(&self) -> bool {
let ret = self.root_use_flags.contains(RootUseFlags::FUNC);
if ret {
debug_assert!(!self.contains(Self::STATIC));
debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC));
}
ret
}
/// Whether the type is used (directly or not) in a function, in return position.
fn is_in_function_return(self) -> bool {
let ret = self.contains(Self::FN_RETURN);
fn is_in_function_return(&self) -> bool {
let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN);
if ret {
debug_assert!(self.is_in_function());
}
ret
}
/// Whether the type is used (directly or not) in a defined function.
/// In other words, whether or not we allow non-FFI-safe types behind a C pointer,
/// to be treated as an opaque type on the other side of the FFI boundary.
fn is_in_defined_function(self) -> bool {
self.contains(Self::DEFINED) && self.is_in_function()
fn is_in_defined_function(&self) -> bool {
self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function()
}
/// Whether the type is used (directly or not) in a function pointer type.
/// Here, we also allow non-FFI-safe types behind a C pointer,
/// to be treated as an opaque type on the other side of the FFI boundary.
fn is_in_fnptr(self) -> bool {
self.contains(Self::THEORETICAL) && self.is_in_function()
fn is_in_fnptr(&self) -> bool {
self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function()
}
/// Whether we can expect type parameters and co in a given type.
fn can_expect_ty_params(self) -> bool {
fn can_expect_ty_params(&self) -> bool {
// rust-defined functions, as well as FnPtrs
self.contains(Self::THEORETICAL) || self.is_in_defined_function()
}
}
bitflags! {
/// Data that summarises how an "outer type" surrounds its inner type(s)
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct OuterTyData: u8 {
/// To show that there is no outer type, the current type is directly used by a `static`
/// variable or a function/FnPtr
const NO_OUTER_TY = 0b01;
/// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically
/// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake
const NO_OUTER_TY_FNPTR = 0b10;
}
}
impl OuterTyData {
/// Get the proper data for a given outer type.
fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self {
match ty.kind() {
ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR,
ty::RawPtr(..)
| ty::Ref(..)
| ty::Adt(..)
| ty::Tuple(..)
| ty::Array(..)
| ty::Slice(_) => Self::empty(),
k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k),
}
self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function()
}
}
@@ -415,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
}
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self {
Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() }
fn new(
cx: &'a LateContext<'tcx>,
base_ty: Unnormalized<'tcx, Ty<'tcx>>,
base_fn_mode: CItemKind,
) -> Self {
ImproperCTypesVisitor {
cx,
base_ty: maybe_normalize_erasing_regions(cx, base_ty),
base_fn_mode,
cache: FxHashSet::default(),
}
}
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
@@ -485,7 +542,7 @@ fn visit_indirection(
{
FfiSafe
} else {
self.visit_type(state, OuterTyData::from_ty(ty), inner_ty)
self.visit_type(state.next(ty), inner_ty)
}
}
}
@@ -505,8 +562,11 @@ fn visit_variant_fields(
let transparent_with_all_zst_fields = if def.repr().transparent() {
if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) {
// Transparent newtypes have at most one non-ZST field which needs to be checked..
let field_ty = get_type_from_field(self.cx, field, args);
match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) {
let field_ty = maybe_normalize_erasing_regions(
self.cx,
Unnormalized::new_wip(field.ty(self.cx.tcx, args)),
);
match self.visit_type(state.next(ty), field_ty) {
FfiUnsafe { ty, .. } if ty.is_unit() => (),
r => return r,
}
@@ -524,8 +584,11 @@ fn visit_variant_fields(
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
let mut all_phantom = !variant.fields.is_empty();
for field in &variant.fields {
let field_ty = get_type_from_field(self.cx, field, args);
all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) {
let field_ty = maybe_normalize_erasing_regions(
self.cx,
Unnormalized::new_wip(field.ty(self.cx.tcx, args)),
);
all_phantom &= match self.visit_type(state.next(ty), field_ty) {
FfiSafe => false,
// `()` fields are FFI-safe!
FfiUnsafe { ty, .. } if ty.is_unit() => false,
@@ -570,7 +633,7 @@ fn visit_struct_or_union(
"consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct"
))
} else {
// FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises
// FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises
Some(msg!(
"consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union"
))
@@ -628,7 +691,7 @@ fn visit_enum(
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() {
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) {
return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty);
return self.visit_type(state.next(ty), inner_ty);
}
return FfiUnsafe {
@@ -660,12 +723,7 @@ fn visit_enum(
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
/// representation which can be exported to C code).
fn visit_type(
&mut self,
state: VisitorState,
outer_ty: OuterTyData,
ty: Ty<'tcx>,
) -> FfiResult<'tcx> {
fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
use FfiResult::*;
let tcx = self.cx.tcx;
@@ -709,7 +767,8 @@ fn visit_type(
// Pattern types are just extra invariants on the type that you need to uphold,
// but only the base type is relevant for being representable in FFI.
// (note: this lint was written when pattern types could only be integers constrained to ranges)
ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty),
// (also note: the lack of ".next(ty)" on the state is on purpose)
ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty),
// types which likely have a stable representation, if the target architecture defines those
// note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64
@@ -740,12 +799,14 @@ fn visit_type(
},
ty::Tuple(tuple) => {
// C functions can return void
let empty_and_safe = tuple.is_empty()
&& outer_ty.contains(OuterTyData::NO_OUTER_TY)
&& state.is_in_function_return();
if empty_and_safe {
if tuple.is_empty()
&& state.is_in_function_return()
&& matches!(
state.outer_ty_kind,
OuterTyKind::None | OuterTyKind::NoneThroughFnPtr
)
{
// C functions can return void
FfiSafe
} else {
FfiUnsafe {
@@ -774,9 +835,8 @@ fn visit_type(
ty::Array(inner_ty, _) => {
if state.is_in_function()
&& outer_ty.contains(OuterTyData::NO_OUTER_TY)
// FIXME(ctypes): VVV-this-VVV shouldn't be the case
&& !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR)
// FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr
&& matches!(state.outer_ty_kind, OuterTyKind::None)
{
// C doesn't really support passing arrays by value - the only way to pass an array by value
// is through a struct.
@@ -788,7 +848,7 @@ fn visit_type(
} else {
// let's allow phantoms to go through,
// since an array of 1-ZSTs is also a 1-ZST
self.visit_type(state, OuterTyData::from_ty(ty), inner_ty)
self.visit_type(state.next(ty), inner_ty)
}
}
@@ -806,19 +866,14 @@ fn visit_type(
let sig = tcx.instantiate_bound_regions_with_erased(sig);
for arg in sig.inputs() {
match self.visit_type(
VisitorState::ARGUMENT_TY_IN_FNPTR,
OuterTyData::from_ty(ty),
*arg,
) {
match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) {
FfiSafe => {}
r => return r,
}
}
let ret_ty = sig.output();
self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty)
self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty)
}
ty::Foreign(..) => FfiSafe,
@@ -886,17 +941,17 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
})
}
fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
let ty = self
.cx
.tcx
.try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty))
.unwrap_or(ty);
fn check_type(
&mut self,
state: VisitorState,
ty: Unnormalized<'tcx, Ty<'tcx>>,
) -> FfiResult<'tcx> {
let ty = maybe_normalize_erasing_regions(self.cx, ty);
if let Some(res) = self.visit_for_opaque_ty(ty) {
return res;
}
self.visit_type(state, OuterTyData::NO_OUTER_TY, ty)
self.visit_type(state, ty)
}
}
@@ -925,7 +980,7 @@ fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
self.spans.push(ty.span);
}
hir::intravisit::walk_ty(self, ty)
hir::intravisit::walk_ty(self, ty);
}
}
@@ -949,6 +1004,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
for (fn_ptr_ty, span) in all_types {
let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty);
let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
// FIXME(ctypes): make a check_for_fnptr
let ffi_res = visitor.check_type(state, fn_ptr_ty);
@@ -970,12 +1026,12 @@ fn check_fn_for_external_abi_fnptr(
let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
let state = VisitorState::argument_from_fnmode(fn_mode);
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg);
self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode);
}
if let hir::FnRetTy::Return(ret_hir) = decl.output {
let state = VisitorState::return_from_fnmode(fn_mode);
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret);
self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode);
}
}
@@ -998,9 +1054,9 @@ fn check_reprc_adt(
}
fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip();
let ty = cx.tcx.type_of(id).instantiate_identity();
let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty);
let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty);
self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
}
@@ -1016,16 +1072,18 @@ fn check_foreign_fn(
let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
let state = VisitorState::argument_from_fnmode(fn_mode);
let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
let ffi_res = visitor.check_type(state, *input_ty);
let input_ty = Unnormalized::new_wip(*input_ty);
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg);
let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode);
let ffi_res = visitor.check_type(state, input_ty);
self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode);
}
if let hir::FnRetTy::Return(ret_hir) = decl.output {
let state = VisitorState::return_from_fnmode(fn_mode);
let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
let ffi_res = visitor.check_type(state, sig.output());
let output_ty = Unnormalized::new_wip(sig.output());
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret);
let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode);
let ffi_res = visitor.check_type(state, output_ty);
self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode);
}
}
@@ -1124,7 +1182,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
| hir::ItemKind::TyAlias(_, _, ty) => {
self.check_type_for_external_abi_fnptr(
cx,
VisitorState::STATIC_TY,
VisitorState::static_entry_point(),
ty,
cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(),
CItemKind::Definition,
@@ -1158,7 +1216,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
self.check_type_for_external_abi_fnptr(
cx,
VisitorState::STATIC_TY,
VisitorState::static_entry_point(),
field.ty,
cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(),
CItemKind::Definition,
+2 -29
View File
@@ -3,12 +3,11 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::stable_hasher::{
StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey,
StableCompare, StableHash, StableHashCtxt, StableHasher,
};
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_hir_id::{HirId, ItemLocalId};
use rustc_hir_id::HirId;
use rustc_macros::{Decodable, Encodable, StableHash};
use rustc_span::def_id::DefPathHash;
pub use rustc_span::edition::Edition;
use rustc_span::{AttrId, Ident, Symbol, sym};
use serde::{Deserialize, Serialize};
@@ -154,23 +153,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl ToStableHashKey for LintExpectationId {
type KeyType = (DefPathHash, ItemLocalId, u16, u16);
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> Self::KeyType {
match self {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx);
(def_path_hash, lint_idx, *attr_index, *lint_index)
}
_ => {
unreachable!("StableHash should only be called for a filled `LintExpectationId`")
}
}
}
}
/// Setting for how to handle a lint.
///
/// See: <https://doc.rust-lang.org/rustc/lints/levels.html>
@@ -623,15 +605,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl ToStableHashKey for LintId {
type KeyType = &'static str;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> &'static str {
self.lint_name_raw()
}
}
impl StableCompare for LintId {
const CAN_USE_UNSTABLE_SORT: bool = true;
@@ -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;
+58 -2
View File
@@ -5,6 +5,7 @@
use std::hash::Hash;
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::sso::SsoHashSet;
use rustc_data_structures::stable_hasher::StableHash;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
use rustc_hir::hir_id::OwnerId;
@@ -256,8 +257,8 @@ fn default_span(&self, _: TyCtxt<'_>) -> Span {
}
impl<'tcx> QueryKey for Ty<'tcx> {
fn default_span(&self, _: TyCtxt<'_>) -> Span {
DUMMY_SP
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
def_id_of_type(*self).map(|def_id| tcx.def_span(def_id)).unwrap_or(DUMMY_SP)
}
}
@@ -360,3 +361,58 @@ fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
self.0.default_span(tcx)
}
}
/// Gets a `DefId` associated with a type
///
/// Visited set is needed to avoid full iteration over
/// deeply nested tuples that have no DefId.
fn def_id_of_type_cached<'a>(ty: Ty<'a>, visited: &mut SsoHashSet<Ty<'a>>) -> Option<DefId> {
match *ty.kind() {
ty::Adt(adt_def, _) => Some(adt_def.did()),
ty::Dynamic(data, ..) => data.principal_def_id(),
ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => {
def_id_of_type_cached(subty, visited)
}
ty::RawPtr(ty, _) => def_id_of_type_cached(ty, visited),
ty::Ref(_, ty, _) => def_id_of_type_cached(ty, visited),
ty::Tuple(tys) => tys.iter().find_map(|ty| {
if visited.insert(ty) {
return def_id_of_type_cached(ty, visited);
}
return None;
}),
ty::FnDef(def_id, _)
| ty::Closure(def_id, _)
| ty::CoroutineClosure(def_id, _)
| ty::Coroutine(def_id, _)
| ty::CoroutineWitness(def_id, _)
| ty::Foreign(def_id) => Some(def_id),
ty::Alias(alias) => Some(alias.kind.def_id()),
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Str
| ty::FnPtr(..)
| ty::UnsafeBinder(_)
| ty::Placeholder(..)
| ty::Param(_)
| ty::Infer(_)
| ty::Bound(..)
| ty::Error(_)
| ty::Never
| ty::Float(_) => None,
}
}
fn def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
def_id_of_type_cached(ty, &mut SsoHashSet::new())
}
+1 -25
View File
@@ -7,11 +7,10 @@
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{
HashingControls, StableHash, StableHashCtxt, StableHasher, ToStableHashKey,
HashingControls, StableHash, StableHashCtxt, StableHasher,
};
use tracing::trace;
use crate::middle::region;
use crate::{mir, ty};
impl<'tcx, H, T> StableHash for &'tcx ty::list::RawList<H, T>
@@ -45,20 +44,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl<'tcx, H, T> ToStableHashKey for &'tcx ty::list::RawList<H, T>
where
T: StableHash,
{
type KeyType = Fingerprint;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> Fingerprint {
let mut hasher = StableHasher::new();
self.stable_hash(hcx, &mut hasher);
hasher.finish()
}
}
impl<'tcx> StableHash for ty::GenericArg<'tcx> {
fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
self.kind().stable_hash(hcx, hasher);
@@ -81,12 +66,3 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
self.into_parts().stable_hash(hcx, hasher);
}
}
impl ToStableHashKey for region::Scope {
type KeyType = region::Scope;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> region::Scope {
*self
}
}
-1
View File
@@ -6,7 +6,6 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
getopts = "0.2"
rand = "0.9.0"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
+23 -38
View File
@@ -14,7 +14,7 @@
use externs::{ExternOpt, split_extern_opt};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey};
use rustc_data_structures::stable_hasher::{StableHasher, StableOrd};
use rustc_errors::emitter::HumanReadableErrorType;
use rustc_errors::{ColorConfig, DiagCtxtFlags};
use rustc_feature::UnstableFeatures;
@@ -628,7 +628,6 @@ pub enum OutputType {
)*
}
impl StableOrd for OutputType {
const CAN_USE_UNSTABLE_SORT: bool = true;
@@ -636,15 +635,6 @@ impl StableOrd for OutputType {
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl ToStableHashKey for OutputType {
type KeyType = Self;
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> Self::KeyType {
*self
}
}
impl OutputType {
pub fn iter_all() -> impl Iterator<Item = OutputType> {
static ALL_VARIANTS: &[OutputType] = &[
@@ -1107,13 +1097,10 @@ pub fn file_for_writing(
outputs: &OutputFilenames,
flavor: OutputType,
codegen_unit_name: &str,
invocation_temp: Option<&str>,
) -> PathBuf {
match *self {
OutFileName::Real(ref path) => path.clone(),
OutFileName::Stdout => {
outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp)
}
OutFileName::Stdout => outputs.temp_path_for_cgu(flavor, codegen_unit_name),
}
}
@@ -1138,6 +1125,17 @@ pub struct OutputFilenames {
filestem: String,
pub single_output_file: Option<OutFileName>,
temps_directory: Option<PathBuf>,
/// A random string generated per invocation of rustc.
///
/// This is prepended to all temporary files so that they do not collide
/// during concurrent invocations of rustc, or past invocations that were
/// preserved with a flag like `-C save-temps`, since these files may be
/// hard linked.
// This does not affect incr comp outputs, only where temp files are stored.
#[stable_hasher(ignore)]
invocation_temp: Option<String>,
explicit_dwo_out_directory: Option<PathBuf>,
pub outputs: OutputTypes,
}
@@ -1180,6 +1178,7 @@ pub fn new(
out_filestem: String,
single_output_file: Option<OutFileName>,
temps_directory: Option<PathBuf>,
invocation_temp: Option<String>,
explicit_dwo_out_directory: Option<PathBuf>,
extra: String,
outputs: OutputTypes,
@@ -1188,6 +1187,7 @@ pub fn new(
out_directory,
single_output_file,
temps_directory,
invocation_temp,
explicit_dwo_out_directory,
outputs,
crate_stem: format!("{out_crate_name}{extra}"),
@@ -1224,23 +1224,14 @@ fn output_path(&self, flavor: OutputType) -> PathBuf {
/// Gets the path where a compilation artifact of the given type for the
/// given codegen unit should be placed on disk. If codegen_unit_name is
/// None, a path distinct from those of any codegen unit will be generated.
pub fn temp_path_for_cgu(
&self,
flavor: OutputType,
codegen_unit_name: &str,
invocation_temp: Option<&str>,
) -> PathBuf {
pub fn temp_path_for_cgu(&self, flavor: OutputType, codegen_unit_name: &str) -> PathBuf {
let extension = flavor.extension();
self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp)
self.temp_path_ext_for_cgu(extension, codegen_unit_name)
}
/// Like `temp_path`, but specifically for dwarf objects.
pub fn temp_path_dwo_for_cgu(
&self,
codegen_unit_name: &str,
invocation_temp: Option<&str>,
) -> PathBuf {
let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp);
pub fn temp_path_dwo_for_cgu(&self, codegen_unit_name: &str) -> PathBuf {
let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name);
if let Some(dwo_out) = &self.explicit_dwo_out_directory {
let mut o = dwo_out.clone();
o.push(p.file_name().unwrap());
@@ -1252,16 +1243,11 @@ pub fn temp_path_dwo_for_cgu(
/// Like `temp_path`, but also supports things where there is no corresponding
/// OutputType, like noopt-bitcode or lto-bitcode.
pub fn temp_path_ext_for_cgu(
&self,
ext: &str,
codegen_unit_name: &str,
invocation_temp: Option<&str>,
) -> PathBuf {
pub fn temp_path_ext_for_cgu(&self, ext: &str, codegen_unit_name: &str) -> PathBuf {
let mut extension = codegen_unit_name.to_string();
// Append `.{invocation_temp}` to ensure temporary files are unique.
if let Some(rng) = invocation_temp {
if let Some(rng) = &self.invocation_temp {
extension.push('.');
extension.push_str(rng);
}
@@ -1302,10 +1288,9 @@ pub fn split_dwarf_path(
split_debuginfo_kind: SplitDebuginfo,
split_dwarf_kind: SplitDwarfKind,
cgu_name: &str,
invocation_temp: Option<&str>,
) -> Option<PathBuf> {
let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp);
let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp);
let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name);
let dwo_out = self.temp_path_dwo_for_cgu(cgu_name);
match (split_debuginfo_kind, split_dwarf_kind) {
(SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
// Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
-16
View File
@@ -5,8 +5,6 @@
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::{env, io};
use rand::{RngCore, rng};
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef};
@@ -163,14 +161,6 @@ pub struct Session {
target_filesearch: FileSearch,
host_filesearch: FileSearch,
/// A random string generated per invocation of rustc.
///
/// This is prepended to all temporary files so that they do not collide
/// during concurrent invocations of rustc, or past invocations that were
/// preserved with a flag like `-C save-temps`, since these files may be
/// hard linked.
pub invocation_temp: Option<String>,
/// The names of intrinsics that the current codegen backend replaces
/// with its own implementations.
pub replaced_intrinsics: FxHashSet<Symbol>,
@@ -1097,11 +1087,6 @@ pub fn build_session(
filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target);
let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host);
let invocation_temp = sopts
.incremental
.as_ref()
.map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string());
let timings = TimingSectionHandler::new(sopts.json_timings);
let sess = Session {
@@ -1132,7 +1117,6 @@ pub fn build_session(
file_depinfo: Default::default(),
target_filesearch,
host_filesearch,
invocation_temp,
replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler`
thin_lto_supported: true, // filled by `run_compiler`
mir_opt_bisect_eval_count: AtomicUsize::new(0),
-18
View File
@@ -465,24 +465,6 @@ fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> DefPathHash
}
}
impl ToStableHashKey for CrateNum {
type KeyType = DefPathHash;
#[inline]
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> DefPathHash {
self.as_def_id().to_stable_hash_key(hcx)
}
}
impl ToStableHashKey for DefPathHash {
type KeyType = DefPathHash;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> DefPathHash {
*self
}
}
macro_rules! typed_def_id {
($Name:ident, $LocalName:ident) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, StableHash)]
+1 -9
View File
@@ -8,7 +8,7 @@
use rustc_arena::DroplessArena;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::stable_hasher::{
StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey,
StableCompare, StableHash, StableHashCtxt, StableHasher,
};
use rustc_data_structures::sync::Lock;
use rustc_macros::{Decodable, Encodable, StableHash, symbols};
@@ -2642,14 +2642,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
}
}
impl ToStableHashKey for Symbol {
type KeyType = String;
#[inline]
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> String {
self.as_str().to_string()
}
}
impl StableCompare for Symbol {
const CAN_USE_UNSTABLE_SORT: bool = true;
-18
View File
@@ -5,12 +5,6 @@
use rustc_ast_ir::Mutability;
#[cfg(feature = "nightly")]
use rustc_data_structures::fingerprint::Fingerprint;
#[cfg(feature = "nightly")]
use rustc_data_structures::stable_hasher::{
StableHash, StableHashCtxt, StableHasher, ToStableHashKey,
};
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash};
use crate::inherent::*;
@@ -48,18 +42,6 @@ pub enum SimplifiedType<DefId> {
Error,
}
#[cfg(feature = "nightly")]
impl<DefId: StableHash> ToStableHashKey for SimplifiedType<DefId> {
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()
}
}
/// Generic parameters are pretty much just bound variables, e.g.
/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as
/// `for<'a, T> fn(&'a T) -> u32`.
+51
View File
@@ -474,6 +474,57 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
self.0.linger()
}
/// Sets the value of the `SO_KEEPALIVE` option on this socket.
///
/// If set to `true`, the operating system will periodically send keepalive
/// probes on an idle connection to verify that the remote peer is still
/// reachable. If the peer fails to respond after a system-determined number
/// of probes, the connection is considered broken and subsequent I/O calls
/// will return an error.
///
/// This is useful for detecting dead peers on long-lived connections where
/// no application-level traffic is exchanged, such as database or SSH
/// connections.
///
/// The timing and frequency of keepalive probes are controlled by
/// system-level settings and are not configured by this method alone.
///
/// # Examples
///
/// ```no_run
/// #![feature(tcp_keepalive)]
///
/// use std::net::TcpStream;
///
/// let stream = TcpStream::connect("127.0.0.1:8080")
/// .expect("Couldn't connect to the server...");
/// stream.set_keepalive(true).expect("set_keepalive call failed");
#[unstable(feature = "tcp_keepalive", issue = "155889")]
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
self.0.set_keepalive(keepalive)
}
/// Gets the value of the `SO_KEEPALIVE` option on this socket.
///
/// For more information about this option, see [`TcpStream::set_keepalive`].
///
/// # Examples
///
/// ```no_run
/// #![feature(tcp_keepalive)]
///
/// use std::net::TcpStream;
///
/// let stream = TcpStream::connect("127.0.0.1:8080")
/// .expect("Couldn't connect to the server...");
/// stream.set_keepalive(true).expect("set_keepalive call failed");
/// assert_eq!(stream.keepalive().unwrap_or(false), true);
/// ```
#[unstable(feature = "tcp_keepalive", issue = "155889")]
pub fn keepalive(&self) -> io::Result<bool> {
self.0.keepalive()
}
/// Sets the value of the `TCP_NODELAY` option on this socket.
///
/// If set, this option disables the Nagle algorithm. This means that
+15
View File
@@ -844,6 +844,21 @@ fn linger() {
assert_eq!(None, t!(stream.linger()));
}
#[test]
#[cfg_attr(target_env = "sgx", ignore)]
#[cfg_attr(target_os = "wasi", ignore)]
fn keepalive() {
let addr = next_test_ip4();
let _listener = t!(TcpListener::bind(&addr));
let stream = t!(TcpStream::connect(&addr));
assert_eq!(false, t!(stream.keepalive()));
t!(stream.set_keepalive(true));
assert_eq!(true, t!(stream.keepalive()));
t!(stream.set_keepalive(false));
assert_eq!(false, t!(stream.keepalive()));
}
#[test]
#[cfg_attr(target_env = "sgx", ignore)]
fn nodelay() {
+19
View File
@@ -1796,6 +1796,25 @@ pub fn into_os_string(self) -> OsString {
self.inner
}
/// Converts the `PathBuf` into a `String` if it contains valid Unicode data.
///
/// On failure, ownership of the original `PathBuf` is returned.
///
/// # Examples
///
/// ```
/// #![feature(pathbuf_into_string)]
/// use std::path::PathBuf;
///
/// let path_buf = PathBuf::from("foo");
/// let string = path_buf.into_string();
/// assert_eq!(string, Ok(String::from("foo")));
/// ```
#[unstable(feature = "pathbuf_into_string", issue = "156203")]
pub fn into_string(self) -> Result<String, PathBuf> {
self.into_os_string().into_string().map_err(PathBuf::from)
}
/// Converts this `PathBuf` into a [boxed](Box) [`Path`].
#[stable(feature = "into_boxed_path", since = "1.20.0")]
#[must_use = "`self` will be dropped if the result is not used"]
+9 -1
View File
@@ -5,7 +5,7 @@
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error};
use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error, unsupported};
use crate::time::Duration;
// We want to re-use as much of Rust's stdlib code as possible,
@@ -127,6 +127,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
moto_rt::net::linger(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
unsupported()
}
pub fn keepalive(&self) -> io::Result<bool> {
unsupported()
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
moto_rt::net::set_nodelay(self.inner.as_raw_fd(), nodelay).map_err(map_motor_error)
}
@@ -219,6 +219,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
sgx_ineffective(None)
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
sgx_ineffective(())
}
pub fn keepalive(&self) -> io::Result<bool> {
sgx_ineffective(false)
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
sgx_ineffective(())
}
@@ -272,6 +272,15 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
}
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_KEEPALIVE, keepalive as c_int) }
}
pub fn keepalive(&self) -> io::Result<bool> {
let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_KEEPALIVE)? };
Ok(raw != 0)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
let value: i32 = if nodelay { 1 } else { 0 };
unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) }
@@ -468,6 +468,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
self.inner.linger()
}
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
self.inner.set_keepalive(keepalive)
}
pub fn keepalive(&self) -> io::Result<bool> {
self.inner.keepalive()
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
@@ -6,6 +6,7 @@
use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut};
use crate::net::{Shutdown, SocketAddr};
use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd};
use crate::sys::pal::unsupported;
use crate::sys::{FromInner, IntoInner, abi};
use crate::time::Duration;
use crate::{cmp, mem, ptr, str};
@@ -332,6 +333,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
unsupported()
}
pub fn keepalive(&self) -> io::Result<bool> {
unsupported()
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) }
}
@@ -456,6 +456,15 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
}
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE, keepalive as c_int) }
}
pub fn keepalive(&self) -> io::Result<bool> {
let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE)? };
Ok(raw != 0)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) }
}
@@ -426,6 +426,15 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
}
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
unsafe { setsockopt(self, c::SOL_SOCKET, c::SO_KEEPALIVE, keepalive as c::BOOL) }
}
pub fn keepalive(&self) -> io::Result<bool> {
let raw: c::BOOL = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_KEEPALIVE)? };
Ok(raw != 0)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
unsafe { setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) }
}
@@ -112,6 +112,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
unsupported()
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
unsupported()
}
pub fn keepalive(&self) -> io::Result<bool> {
unsupported()
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unsupported()
}
@@ -87,6 +87,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
self.0
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
self.0
}
pub fn keepalive(&self) -> io::Result<bool> {
self.0
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
self.0
}
@@ -145,6 +145,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
unsupported()
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
unsupported()
}
pub fn keepalive(&self) -> io::Result<bool> {
unsupported()
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unsupported()
}
@@ -353,6 +353,14 @@ pub fn linger(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn set_keepalive(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn keepalive(&self) -> io::Result<bool> {
unimpl!();
}
pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> {
crate::os::xous::ffi::blocking_scalar(
services::net_server(),
@@ -2388,6 +2388,7 @@ SleepConditionVariableSRW
SleepEx
SO_BROADCAST
SO_ERROR
SO_KEEPALIVE
SO_LINGER
SO_RCVTIMEO
SO_SNDTIMEO
@@ -3189,6 +3189,7 @@ fn default() -> Self {
pub const SOL_SOCKET: i32 = 65535i32;
pub const SO_BROADCAST: i32 = 32i32;
pub const SO_ERROR: i32 = 4103i32;
pub const SO_KEEPALIVE: i32 = 8i32;
pub const SO_LINGER: i32 = 128i32;
pub const SO_RCVTIMEO: i32 = 4102i32;
pub const SO_SNDTIMEO: i32 = 4101i32;
+1 -2
View File
@@ -1,8 +1,7 @@
//~ ERROR: cycle detected when computing layout of
use std::mem;
pub struct S<T: Tr> {
//~^ ERROR: cycle detected when computing layout of
pub f: <T as Tr>::I,
}
@@ -1,6 +1,14 @@
error[E0391]: cycle detected when computing layout of `S<S<()>>`
--> tests/fail/layout_cycle.rs:LL:CC
|
= note: ...which requires computing layout of `<S<()> as Tr>::I`...
LL | pub struct S<T: Tr> {
| ^^^^^^^^^^^^^^^^^^^
|
note: ...which requires computing layout of `<S<()> as Tr>::I`...
--> tests/fail/layout_cycle.rs:LL:CC
|
LL | type I: Tr;
| ^^^^^^^^^^
= note: ...which again requires computing layout of `S<S<()>>`, completing the cycle
note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE`
--> RUSTLIB/core/src/mem/mod.rs:LL:CC
@@ -0,0 +1,45 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "cfi-types"
version = "0.0.8"
[[package]]
name = "cross-lang-cfi-types-crate-abort"
version = "0.1.0"
dependencies = [
"cfi-types",
]
[[package]]
name = "cross-lang-cfi-types-crate-not-abort"
version = "0.1.0"
dependencies = [
"cfi-types",
]
[[package]]
name = "indirect-arity-mismatch-abort"
version = "0.1.0"
[[package]]
name = "indirect-pointee-type-mismatch-abort"
version = "0.1.0"
[[package]]
name = "indirect-return-type-mismatch-abort"
version = "0.1.0"
[[package]]
name = "indirect-type-mismatch-abort"
version = "0.1.0"
[[package]]
name = "indirect-type-qualifier-mismatch-abort"
version = "0.1.0"
[[package]]
name = "invalid-branch-target-abort"
version = "0.1.0"
@@ -0,0 +1,13 @@
# Workspace mirroring the examples in <https://github.com/rcvalle/rust-cfi-examples>.
[workspace]
resolver = "2"
members = [
"invalid-branch-target-abort",
"indirect-arity-mismatch-abort",
"indirect-pointee-type-mismatch-abort",
"indirect-return-type-mismatch-abort",
"indirect-type-qualifier-mismatch-abort",
"indirect-type-mismatch-abort",
"cross-lang-cfi-types-crate-abort",
"cross-lang-cfi-types-crate-not-abort",
]
@@ -0,0 +1,7 @@
[package]
name = "cross-lang-cfi-types-crate-abort"
version = "0.1.0"
edition = "2021"
[dependencies]
cfi-types = { path = "../vendor/cfi-types" }
@@ -0,0 +1,5 @@
include!("../shared_build_rs.rs");
fn main() {
build_foo_static_lib(&[]);
}
@@ -0,0 +1,5 @@
int
do_twice(int (*fn)(int), int arg)
{
return fn(arg) + fn(arg);
}
@@ -0,0 +1,36 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with different return and parameter types than the
// return type expected and arguments intended/passed at the call/branch site,
// across the FFI boundary using the `cfi_types` crate for cross-language LLVM
// CFI.
use std::mem;
use cfi_types::{c_int, c_long};
#[link(name = "foo")]
unsafe extern "C" {
fn do_twice(f: unsafe extern "C" fn(c_int) -> c_int, arg: i32) -> i32;
}
unsafe extern "C" fn add_one(x: c_int) -> c_int {
c_int(x.0 + 1)
}
unsafe extern "C" fn add_two(x: c_long) -> c_long {
c_long(x.0 + 2)
}
fn main() {
let answer = unsafe { do_twice(add_one, 5) };
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: unsafe extern "C" fn(c_int) -> c_int = unsafe {
mem::transmute::<*const u8, unsafe extern "C" fn(c_int) -> c_int>(add_two as *const u8)
};
let next_answer = unsafe { do_twice(f, 5) };
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,7 @@
[package]
name = "cross-lang-cfi-types-crate-not-abort"
version = "0.1.0"
edition = "2021"
[dependencies]
cfi-types = { path = "../vendor/cfi-types" }
@@ -0,0 +1,5 @@
include!("../shared_build_rs.rs");
fn main() {
build_foo_static_lib(&[]);
}
@@ -0,0 +1,23 @@
#include <stdio.h>
#include <stdlib.h>
// This definition has the type id "_ZTSFvlE".
void
hello_from_c(long arg)
{
printf("Hello from C!\n");
}
// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the
// purposes of this example.
void
indirect_call_from_c(void (*fn)(long), long arg)
{
// This call site tests whether the destination pointer is a member of the
// group derived from the same type id of the fn declaration, which has the
// type id "_ZTSFvlE".
//
// Notice that since the test is at the call site and generated by Clang,
// the type id used in the test is encoded by Clang.
fn(arg);
}
@@ -0,0 +1,67 @@
use cfi_types::c_long;
#[link(name = "foo")]
extern "C" {
// This declaration has the type id "_ZTSFvlE" because it uses the CFI types
// for cross-language LLVM CFI support. The cfi_types crate provides a new
// set of C types as user-defined types using the cfi_encoding attribute and
// repr(transparent) to be used for cross-language LLVM CFI support. This
// new set of C types allows the Rust compiler to identify and correctly
// encode C types in extern "C" function types indirectly called across the
// FFI boundary when CFI is enabled.
fn hello_from_c(_: c_long);
// This declaration has the type id "_ZTSFvPFvlElE" because it uses the CFI
// types for cross-language LLVM CFI support--this can be ignored for the
// purposes of this example.
fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long);
}
// This definition has the type id "_ZTSFvlE" because it uses the CFI types for
// cross-language LLVM CFI support, similarly to the hello_from_c declaration
// above.
unsafe extern "C" fn hello_from_rust(_: c_long) {
println!("Hello, world!");
}
// This definition has the type id "_ZTSFvlE" because it uses the CFI types for
// cross-language LLVM CFI support, similarly to the hello_from_c declaration
// above.
unsafe extern "C" fn hello_from_rust_again(_: c_long) {
println!("Hello from Rust again!");
}
// This definition would also have the type id "_ZTSFvPFvlElE" because it uses
// the CFI types for cross-language LLVM CFI support, similarly to the
// hello_from_c declaration above--this can be ignored for the purposes of this
// example.
fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) {
// This indirect call site tests whether the destination pointer is a member
// of the group derived from the same type id of the f declaration, which
// has the type id "_ZTSFvlE" because it uses the CFI types for
// cross-language LLVM CFI support, similarly to the hello_from_c
// declaration above.
unsafe { f(arg) }
}
// This definition has the type id "_ZTSFvvE"--this can be ignored for the
// purposes of this example.
fn main() {
// This demonstrates an indirect call within Rust-only code using the same
// encoding for hello_from_rust and the test at the indirect call site at
// indirect_call (i.e., "_ZTSFvlE").
indirect_call(hello_from_rust, c_long(5));
// This demonstrates an indirect call across the FFI boundary with the Rust
// compiler and Clang using the same encoding for hello_from_c and the test
// at the indirect call site at indirect_call (i.e., "_ZTSFvlE").
indirect_call(hello_from_c, c_long(5));
// This demonstrates an indirect call to a function passed as a callback
// across the FFI boundary with the Rust compiler and Clang the same
// encoding for the passed-callback declaration and the test at the indirect
// call site at indirect_call_from_c (i.e., "_ZTSFvlE").
unsafe {
indirect_call_from_c(hello_from_rust_again, c_long(5));
}
}
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "cross-lang-integer-normalization-abort"
version = "0.1.0"
@@ -0,0 +1,10 @@
[package]
name = "cross-lang-integer-normalization-abort"
version = "0.1.0"
edition = "2021"
# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can
# be built with different `RUSTFLAGS` (i.e., integer normalization).
[workspace]
members = ["."]
resolver = "2"
@@ -0,0 +1,5 @@
include!("../shared_build_rs.rs");
fn main() {
build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]);
}
@@ -0,0 +1,5 @@
int
do_twice(int (*fn)(int), int arg)
{
return fn(arg) + fn(arg);
}
@@ -0,0 +1,34 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with different return and parameter types than the
// return type expected and arguments intended/passed at the call/branch site,
// across the FFI boundary using integer normalization for cross-language LLVM
// CFI.
use std::mem;
#[link(name = "foo")]
extern "C" {
fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32;
}
unsafe extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
unsafe extern "C" fn add_two(x: i64) -> i64 {
x + 2
}
fn main() {
let answer = unsafe { do_twice(add_one, 5) };
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: unsafe extern "C" fn(i32) -> i32 = unsafe {
mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8)
};
let next_answer = unsafe { do_twice(f, 5) };
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "cross-lang-integer-normalization-not-abort"
version = "0.1.0"
@@ -0,0 +1,10 @@
[package]
name = "cross-lang-integer-normalization-not-abort"
version = "0.1.0"
edition = "2021"
# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can
# be built with different `RUSTFLAGS` (i.e., integer normalization).
[workspace]
members = ["."]
resolver = "2"
@@ -0,0 +1,5 @@
include!("../shared_build_rs.rs");
fn main() {
build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]);
}
@@ -0,0 +1,23 @@
#include <stdio.h>
#include <stdlib.h>
// This definition has the type id "_ZTSFvlE".
void
hello_from_c(long arg)
{
printf("Hello from C!\n");
}
// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the
// purposes of this example.
void
indirect_call_from_c(void (*fn)(long), long arg)
{
// This call site tests whether the destination pointer is a member of the
// group derived from the same type id of the fn declaration, which has the
// type id "_ZTSFvlE".
//
// Notice that since the test is at the call site and generated by Clang,
// the type id used in the test is encoded by Clang.
fn(arg);
}
@@ -0,0 +1,89 @@
use std::ffi::c_long;
#[link(name = "foo")]
extern "C" {
// This declaration would have the type id "_ZTSFvlE", but at the time types
// are encoded, all type aliases are already resolved to their respective
// Rust aliased types, so this is encoded either as "_ZTSFvu3i32E" or
// "_ZTSFvu3i64E" depending to what type c_long type alias is resolved to,
// which currently uses the u<length><type-name> vendor extended type
// encoding for the Rust integer types--this is the problem demonstrated in
// this example.
fn hello_from_c(_: c_long);
// This declaration would have the type id "_ZTSFvPFvlElE", but is encoded
// either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E"
// (compressed), similarly to the hello_from_c declaration above--this can
// be ignored for the purposes of this example.
fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long);
}
// This definition would have the type id "_ZTSFvlE", but is encoded either as
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration
// above.
unsafe extern "C" fn hello_from_rust(_: c_long) {
println!("Hello, world!");
}
// This definition would have the type id "_ZTSFvlE", but is encoded either as
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration
// above.
unsafe extern "C" fn hello_from_rust_again(_: c_long) {
println!("Hello from Rust again!");
}
// This definition would also have the type id "_ZTSFvPFvlElE", but is encoded
// either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E"
// (compressed), similarly to the hello_from_c declaration above--this can be
// ignored for the purposes of this example.
fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) {
// This indirect call site tests whether the destination pointer is a member
// of the group derived from the same type id of the f declaration, which
// would have the type id "_ZTSFvlE", but is encoded either as
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c
// declaration above.
//
// Notice that since the test is at the call site and generated by the Rust
// compiler, the type id used in the test is encoded by the Rust compiler.
unsafe { f(arg) }
}
// This definition has the type id "_ZTSFvvE"--this can be ignored for the
// purposes of this example.
fn main() {
// This demonstrates an indirect call within Rust-only code using the same
// encoding for hello_from_rust and the test at the indirect call site at
// indirect_call (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E").
indirect_call(hello_from_rust, 5);
// This demonstrates an indirect call across the FFI boundary with the Rust
// compiler and Clang using different encodings for hello_from_c and the
// test at the indirect call site at indirect_call (i.e., "_ZTSFvu3i32E" or
// "_ZTSFvu3i64E" vs "_ZTSFvlE").
//
// When using rustc LTO (i.e., -Clto), this works because the type id used
// is from the Rust-declared hello_from_c, which is encoded by the Rust
// compiler (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E").
//
// When using (proper) LTO (i.e., -Clinker-plugin-lto), this does not work
// because the type id used is from the C-defined hello_from_c, which is
// encoded by Clang (i.e., "_ZTSFvlE").
indirect_call(hello_from_c, 5);
// This demonstrates an indirect call to a function passed as a callback
// across the FFI boundary with the Rust compiler and Clang using different
// encodings for the passed-callback declaration and the test at the
// indirect call site at indirect_call_from_c (i.e., "_ZTSFvu3i32E" or
// "_ZTSFvu3i64E" vs "_ZTSFvlE").
//
// When Rust functions are passed as callbacks across the FFI boundary to be
// called back from C code, the tests are also at the call site but
// generated by Clang instead, so the type ids used in the tests are encoded
// by Clang, which will not match the type ids of declarations encoded by
// the Rust compiler (e.g., hello_from_rust_again). (The same happens the
// other way around for C functions passed as callbacks across the FFI
// boundary to be called back from Rust code.)
unsafe {
indirect_call_from_c(hello_from_rust_again, 5);
}
}
@@ -0,0 +1,4 @@
[package]
name = "indirect-arity-mismatch-abort"
version = "0.1.0"
edition = "2021"
@@ -0,0 +1,30 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with a different number of parameters than
// arguments intended/passed at the call/branch site.
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
fn add_two(x: i32, _y: i32) -> i32 {
x + 2
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,4 @@
[package]
name = "indirect-pointee-type-mismatch-abort"
version = "0.1.0"
edition = "2021"
@@ -0,0 +1,32 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with different return and parameter (i.e., pointee)
// types than the return type expected and arguments intended/passed at the
// call/branch site.
use std::mem;
fn add_one(x: *const i32) -> i32 {
unsafe { *x + 1 }
}
fn add_two(x: *const i64) -> i32 {
unsafe { (*x + 2) as i32 }
}
fn do_twice(f: fn(*const i32) -> i32, arg: *const i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let value: i32 = 5;
let answer = do_twice(add_one, &value);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(*const i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(*const i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, &value);
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,4 @@
[package]
name = "indirect-return-type-mismatch-abort"
version = "0.1.0"
edition = "2021"
@@ -0,0 +1,30 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with a different return type than the return type
// expected at the call/branch site.
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
fn add_two(x: i32) -> i64 {
i64::from(x + 2)
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,4 @@
[package]
name = "indirect-type-mismatch-abort"
version = "0.1.0"
edition = "2021"
@@ -0,0 +1,30 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with different return and parameter types than the
// return type expected and arguments intended/passed at the call/branch site.
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
fn add_two(x: i64) -> i64 {
x + 2
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,4 @@
[package]
name = "indirect-type-qualifier-mismatch-abort"
version = "0.1.0"
edition = "2021"
@@ -0,0 +1,31 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to a function with parameter type qualifiers than the argument
// type qualifiers intended/passed at the call/branch site.
use std::mem;
fn add_one(x: &i32) -> i32 {
*x + 1
}
fn add_two(x: &mut i32) -> i32 {
*x + 2
}
fn do_twice(f: fn(&i32) -> i32, arg: &i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let value: i32 = 5;
let answer = do_twice(add_one, &value);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(&i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(&i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, &value);
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,4 @@
[package]
name = "invalid-branch-target-abort"
version = "0.1.0"
edition = "2021"
@@ -0,0 +1,50 @@
// This example demonstrates redirecting control flow using an indirect
// branch/call to an invalid destination (i.e., within the body of the
// function).
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
#[unsafe(naked)]
pub extern "C" fn add_two(_x: i32) -> ! {
// x + 2 preceded by a landing pad/nop block
core::arch::naked_asm!(
r#"
nop
nop
nop
nop
nop
nop
nop
nop
nop
lea eax, [rdi + 2]
ret
"#,
);
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 = unsafe {
// Offset 0 is a valid branch/call destination (i.e., the function entry
// point), but offsets 1-8 within the landing pad/nop block are invalid
// branch/call destinations (i.e., within the body of the function).
mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5))
};
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
@@ -0,0 +1,151 @@
//! Verifies that the examples in <https://github.com/rcvalle/rust-cfi-examples> build and run with
//!`-Zbuild-std` to prevent regressions such as [rust-lang/rust#142284].
//@ needs-sanitizer-cfi
//@ needs-force-clang-based-tests
//@ needs-rust-lld
//@ needs-target-std
//@ ignore-cross-compile
//@ only-x86_64-unknown-linux-gnu
#![deny(warnings)]
use std::path::Path;
use run_make_support::external_deps::rustc::sysroot as rustc_sysroot;
use run_make_support::run::cmd;
use run_make_support::{bin_name, cargo, path, target};
fn clang_path() -> String {
if let Ok(d) = std::env::var("LLVM_BIN_DIR") {
let clang = Path::new(d.trim_end_matches('/')).join("clang");
if clang.exists() {
return clang.display().to_string();
}
}
if let Ok(clang) = std::env::var("CLANG") {
let clang = Path::new(clang.trim_end_matches('/'));
if clang.exists() {
return clang.display().to_string();
}
}
"clang".to_string()
}
fn fuse_ld_path() -> String {
if let Ok(d) = std::env::var("LLVM_BIN_DIR") {
let llvm_bin_dir = Path::new(d.trim_end_matches('/'));
let gcc_ld_lld = llvm_bin_dir.join("gcc-ld").join("ld.lld");
if gcc_ld_lld.exists() {
return gcc_ld_lld.display().to_string();
}
let ld_lld = llvm_bin_dir.join("ld.lld");
if ld_lld.exists() {
return ld_lld.display().to_string();
}
}
if let Ok(clang) = std::env::var("CLANG") {
let clang = Path::new(clang.trim_end_matches('/'));
if let Some(clang_dir) = clang.parent() {
let gcc_ld_lld = clang_dir.join("gcc-ld").join("ld.lld");
if gcc_ld_lld.exists() {
return gcc_ld_lld.display().to_string();
}
let ld_lld = clang_dir.join("ld.lld");
if ld_lld.exists() {
return ld_lld.display().to_string();
}
}
}
let target_bin_dir = rustc_sysroot().join("lib").join("rustlib").join(target()).join("bin");
let gcc_ld_lld = target_bin_dir.join("gcc-ld").join("ld.lld");
if gcc_ld_lld.exists() {
return gcc_ld_lld.display().to_string();
}
"ld.lld".to_string()
}
fn run_and_expect_cfi_abort(target_dir: &Path, target: &str, binary: &str) {
let exe = target_dir.join(target).join("release").join(bin_name(binary));
let output = cmd(&exe).run_fail();
output
.assert_stdout_contains("With CFI enabled, you should not see the next answer")
.assert_stdout_not_contains("The next answer is:");
}
fn run_and_expect_cfi_not_abort(target_dir: &Path, target: &str, binary: &str) {
let exe = target_dir.join(target).join("release").join(bin_name(binary));
let output = cmd(&exe).run();
output.assert_stdout_contains("Hello from C!");
}
fn main() {
let clang = clang_path();
let fuse_ld = fuse_ld_path();
let tgt = target();
let target_dir = path("target");
let lib = std::env::var("LIB").unwrap_or_default();
let prior_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
let rustflags = format!(
"{prior_rustflags} -Clinker-plugin-lto -Clinker={clang} \
-Clink-arg=-fuse-ld={fuse_ld} -Zsanitizer=cfi \
-Ctarget-feature=-crt-static"
)
.trim()
.to_owned();
let rustflags_with_integer_normalization =
format!("{rustflags} -Zsanitizer-cfi-normalize-integers").trim().to_owned();
let run = |manifest: &Path, rustflags: &str, workspace: bool| {
let mut c = cargo();
c.arg("build")
.arg("--manifest-path")
.arg(manifest)
.arg("--release")
.arg("-Zbuild-std")
.arg("--target")
.arg(&tgt);
if workspace {
c.arg("--workspace");
}
c.env("RUSTFLAGS", rustflags)
.env("CC", &clang)
.env("CARGO_TARGET_DIR", &target_dir)
.env("RUSTC_BOOTSTRAP", "1")
.env("LIB", &lib)
.run();
};
run(Path::new("Cargo.toml"), &rustflags, true);
for bin in [
"invalid-branch-target-abort",
"indirect-arity-mismatch-abort",
"indirect-pointee-type-mismatch-abort",
"indirect-return-type-mismatch-abort",
"indirect-type-qualifier-mismatch-abort",
"indirect-type-mismatch-abort",
"cross-lang-cfi-types-crate-abort",
] {
run_and_expect_cfi_abort(&target_dir, &tgt, bin);
}
for bin in ["cross-lang-cfi-types-crate-not-abort"] {
run_and_expect_cfi_not_abort(&target_dir, &tgt, bin);
}
run(
Path::new("cross-lang-integer-normalization-abort/Cargo.toml"),
&rustflags_with_integer_normalization,
false,
);
run_and_expect_cfi_abort(&target_dir, &tgt, "cross-lang-integer-normalization-abort");
run(
Path::new("cross-lang-integer-normalization-not-abort/Cargo.toml"),
&rustflags_with_integer_normalization,
false,
);
run_and_expect_cfi_not_abort(&target_dir, &tgt, "cross-lang-integer-normalization-not-abort");
}
@@ -0,0 +1,73 @@
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
fn clang_path() -> PathBuf {
if let Ok(d) = env::var("LLVM_BIN_DIR") {
let clang = Path::new(d.trim_end_matches('/')).join("clang");
if clang.exists() {
return clang;
}
}
if let Ok(clang) = env::var("CLANG") {
let clang = Path::new(clang.trim_end_matches('/'));
if clang.exists() {
return clang.to_path_buf();
}
}
PathBuf::from("clang")
}
fn llvm_ar_path() -> PathBuf {
if let Ok(d) = env::var("LLVM_BIN_DIR") {
let llvm_ar = Path::new(d.trim_end_matches('/')).join("llvm-ar");
if llvm_ar.exists() {
return llvm_ar;
}
}
if let Ok(clang) = env::var("CLANG") {
let clang = Path::new(&clang);
if let Some(clang_dir) = clang.parent() {
let llvm_ar = clang_dir.join("llvm-ar");
if llvm_ar.exists() {
return llvm_ar;
}
}
}
PathBuf::from("llvm-ar")
}
fn build_foo_static_lib(extra_flags: &[&str]) {
let out_dir = env::var("OUT_DIR").expect("OUT_DIR");
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR");
let c_src = Path::new(&manifest_dir).join("src/foo.c");
let bc_path = Path::new(&out_dir).join("foo.bc");
let a_path = Path::new(&out_dir).join("libfoo.a");
let clang = clang_path();
let llvm_ar = llvm_ar_path();
let mut clang_args = vec!["-Wall", "-flto=thin", "-fsanitize=cfi"];
clang_args.extend_from_slice(extra_flags);
clang_args.extend_from_slice(&["-fvisibility=hidden", "-c", "-emit-llvm", "-o"]);
let st = Command::new(&clang)
.args(&clang_args)
.arg(&bc_path)
.arg(&c_src)
.status()
.unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", clang.display()));
assert!(st.success(), "`{}` failed with {st}", clang.display());
let st = Command::new(&llvm_ar)
.args(["rcs", a_path.to_str().unwrap(), bc_path.to_str().unwrap()])
.status()
.unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", llvm_ar.display()));
assert!(st.success(), "`{}` failed with {st}", llvm_ar.display());
println!("cargo:rustc-link-search=native={out_dir}");
println!("cargo:rustc-link-lib=static=foo");
println!("cargo:rerun-if-changed={}", c_src.display());
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=../shared_build_rs.rs");
}
@@ -0,0 +1,13 @@
[package]
name = "cfi-types"
version = "0.0.8"
edition = "2021"
description = "CFI types for cross-language LLVM CFI support"
homepage = "https://github.com/rcvalle/rust-crate-cfi-types"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rcvalle/rust-crate-cfi-types"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
@@ -0,0 +1,96 @@
//! CFI types for cross-language LLVM CFI support.
//!
//! The cfi_types crate provides a new set of C types as user-defined types
//! using the cfi_encoding attribute and repr(transparent) to be used for
//! cross-language LLVM CFI support. This new set of C types allows the Rust
//! compiler to identify and correctly encode C types in extern "C" function
//! types indirectly called across the FFI boundary when CFI is enabled.
//!
//! The use of these types are optional and are recommended for when enforcement
//! and explicitness of types used across the FFI boundary and no loss of
//! granularity for cross-language LLVM CFI are preferred.
//!
//! Alternatively, the `-Zsanitizer-cfi-normalize-integers` option may be used
//! with the Clang `-fsanitize-cfi-icall-experimental-normalize-integers` option
//! for cross-language LLVM CFI support.
#![feature(cfg_sanitizer_cfi)]
#![feature(cfi_encoding)]
#![allow(non_camel_case_types)]
/// CFI type equivalent to Rust's core::ffi::c_char type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "c")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_char(pub core::ffi::c_char);
/// CFI type equivalent to Rust's core::ffi::c_int type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "i")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_int(pub core::ffi::c_int);
/// CFI type equivalent to Rust's core::ffi::c_long type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "l")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_long(pub core::ffi::c_long);
/// CFI type equivalent to Rust's core::ffi::c_longlong type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "x")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_longlong(pub core::ffi::c_longlong);
/// CFI type equivalent to Rust's core::ffi::c_schar type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "a")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_schar(pub core::ffi::c_schar);
/// CFI type equivalent to Rust's core::ffi::c_short type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "s")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_short(pub core::ffi::c_short);
/// CFI type equivalent to Rust's core::ffi::c_uchar type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "h")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_uchar(pub core::ffi::c_uchar);
/// CFI type equivalent to Rust's core::ffi::c_uint type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "j")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_uint(pub core::ffi::c_uint);
/// CFI type equivalent to Rust's core::ffi::c_ulong type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "m")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_ulong(pub core::ffi::c_ulong);
/// CFI type equivalent to Rust's core::ffi::c_ulonglong type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "y")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_ulonglong(pub core::ffi::c_ulonglong);
/// CFI type equivalent to Rust's core::ffi::c_ushort type alias.
#[allow(dead_code)]
#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "t")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(transparent)]
pub struct c_ushort(pub core::ffi::c_ushort);
@@ -37,7 +37,7 @@ fn target_cpu(&self, _sess: &Session) -> String {
"fake_target_cpu".to_owned()
}
fn codegen_crate(&self, _tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box<dyn Any> {
fn codegen_crate(&self, _tcx: TyCtxt<'_>) -> Box<dyn Any> {
Box::new(CompiledModules { modules: vec![], allocator_module: None })
}
@@ -46,6 +46,7 @@ fn join_codegen(
ongoing_codegen: Box<dyn Any>,
_sess: &Session,
_outputs: &OutputFilenames,
_crate_info: &CrateInfo,
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
let codegen_results = ongoing_codegen
.downcast::<CompiledModules>()
@@ -1,5 +1,5 @@
error: overflow evaluating associated type `T::This`
--> $DIR/normalization-overflow.rs:9:5
--> $DIR/normalization-overflow.rs:14:5
|
LL | type This = Self::This;
| ^^^^^^^^^
@@ -0,0 +1,9 @@
error[E0271]: type mismatch resolving `T::This normalizes-to _`
--> $DIR/normalization-overflow.rs:14:5
|
LL | type This = Self::This;
| ^^^^^^^^^ types differ
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.
@@ -1,12 +1,19 @@
#![feature(inherent_associated_types)]
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@ [next] compile-flags: -Znext-solver
#![feature(inherent_associated_types, rustc_attrs)]
#![allow(incomplete_features)]
#![rustc_no_implicit_bounds]
// FIXME(fmease): I'd prefer to report a cycle error here instead of an overflow one.
struct T;
impl T {
type This = Self::This; //~ ERROR overflow evaluating associated type `T::This`
type This = Self::This;
//[current]~^ ERROR: overflow evaluating associated type `T::This`
//[next]~^^ ERROR: type mismatch resolving `T::This normalizes-to _`
}
fn main() {}
+5 -1
View File
@@ -10,7 +10,11 @@ note: ...which requires simplifying constant for the type system `core::mem::Siz
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
note: ...which requires const-evaluating + checking `core::mem::SizedTypeProperties::SIZE`...
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
= note: ...which requires computing layout of `Foo`...
note: ...which requires computing layout of `Foo`...
--> $DIR/const-size_of-cycle.rs:1:1
|
LL | struct Foo {
| ^^^^^^^^^^
= note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
--> $DIR/const-size_of-cycle.rs:2:17
+5 -1
View File
@@ -9,7 +9,11 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
|
LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
note: ...which requires computing layout of `Foo`...
--> $DIR/issue-44415.rs:5:1
|
LL | struct Foo {
| ^^^^^^^^^^
= note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
--> $DIR/issue-44415.rs:6:17
@@ -0,0 +1,21 @@
error[E0271]: type mismatch resolving `X3 normalizes-to _`
--> $DIR/infinite-type-alias-mutual-recursion.rs:12:1
|
LL | type X1 = X2;
| ^^^^^^^ types differ
error[E0271]: type mismatch resolving `X1 normalizes-to _`
--> $DIR/infinite-type-alias-mutual-recursion.rs:16:1
|
LL | type X2 = X3;
| ^^^^^^^ types differ
error[E0271]: type mismatch resolving `X2 normalizes-to _`
--> $DIR/infinite-type-alias-mutual-recursion.rs:19:1
|
LL | type X3 = X1;
| ^^^^^^^ types differ
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0271`.
@@ -0,0 +1,27 @@
error[E0275]: overflow normalizing the type alias `X2`
--> $DIR/infinite-type-alias-mutual-recursion.rs:12:1
|
LL | type X1 = X2;
| ^^^^^^^
|
= note: in case this is a recursive type alias, consider using a struct, enum, or union instead
error[E0275]: overflow normalizing the type alias `X3`
--> $DIR/infinite-type-alias-mutual-recursion.rs:16:1
|
LL | type X2 = X3;
| ^^^^^^^
|
= note: in case this is a recursive type alias, consider using a struct, enum, or union instead
error[E0275]: overflow normalizing the type alias `X1`
--> $DIR/infinite-type-alias-mutual-recursion.rs:19:1
|
LL | type X3 = X1;
| ^^^^^^^
|
= note: in case this is a recursive type alias, consider using a struct, enum, or union instead
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0275`.
@@ -0,0 +1,30 @@
error[E0391]: cycle detected when expanding type alias `X1`
--> $DIR/infinite-type-alias-mutual-recursion.rs:12:11
|
LL | type X1 = X2;
| ^^
|
note: ...which requires expanding type alias `X2`...
--> $DIR/infinite-type-alias-mutual-recursion.rs:16:11
|
LL | type X2 = X3;
| ^^
note: ...which requires expanding type alias `X3`...
--> $DIR/infinite-type-alias-mutual-recursion.rs:19:11
|
LL | type X3 = X1;
| ^^
= note: ...which again requires expanding type alias `X1`, completing the cycle
= note: type aliases cannot be recursive
= help: consider using a struct, enum, or union instead to break the cycle
= help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
note: cycle used when checking that `X1` is well-formed
--> $DIR/infinite-type-alias-mutual-recursion.rs:12:1
|
LL | type X1 = X2;
| ^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0391`.
@@ -0,0 +1,30 @@
error[E0391]: cycle detected when expanding type alias `X1`
--> $DIR/infinite-type-alias-mutual-recursion.rs:12:11
|
LL | type X1 = X2;
| ^^
|
note: ...which requires expanding type alias `X2`...
--> $DIR/infinite-type-alias-mutual-recursion.rs:16:11
|
LL | type X2 = X3;
| ^^
note: ...which requires expanding type alias `X3`...
--> $DIR/infinite-type-alias-mutual-recursion.rs:19:11
|
LL | type X3 = X1;
| ^^
= note: ...which again requires expanding type alias `X1`, completing the cycle
= note: type aliases cannot be recursive
= help: consider using a struct, enum, or union instead to break the cycle
= help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
note: cycle used when checking that `X1` is well-formed
--> $DIR/infinite-type-alias-mutual-recursion.rs:12:1
|
LL | type X1 = X2;
| ^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0391`.

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