mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-08 09:38:26 +03:00
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:
+1
-1
@@ -4101,6 +4101,7 @@ dependencies = [
|
||||
name = "rustc_interface"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rand 0.9.2",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_lowering",
|
||||
@@ -4602,7 +4603,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"libc",
|
||||
"rand 0.9.2",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
|
||||
|
||||
@@ -209,12 +209,12 @@ fn target_cpu(&self, sess: &Session) -> String {
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box<dyn Any> {
|
||||
fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
|
||||
info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE));
|
||||
let config = self.config.get().unwrap();
|
||||
if config.jit_mode {
|
||||
#[cfg(feature = "jit")]
|
||||
driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone());
|
||||
driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone());
|
||||
|
||||
#[cfg(not(feature = "jit"))]
|
||||
tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift");
|
||||
@@ -228,6 +228,7 @@ fn join_codegen(
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
sess: &Session,
|
||||
outputs: &OutputFilenames,
|
||||
_crate_info: &CrateInfo,
|
||||
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs)
|
||||
}
|
||||
|
||||
@@ -29,16 +29,8 @@ pub(crate) fn codegen(
|
||||
let lto_mode = module.module_llvm.lto_mode;
|
||||
let lto_supported = module.module_llvm.lto_supported;
|
||||
|
||||
let bc_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Bitcode,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let obj_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Object,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name);
|
||||
let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name);
|
||||
|
||||
if config.bitcode_needed() {
|
||||
let _timer =
|
||||
@@ -82,22 +74,15 @@ pub(crate) fn codegen(
|
||||
}
|
||||
|
||||
if config.emit_ir {
|
||||
let out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::LlvmAssembly,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let out =
|
||||
cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name);
|
||||
std::fs::write(out, "").expect("write file");
|
||||
}
|
||||
|
||||
if config.emit_asm {
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
|
||||
let path = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Assembly,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name);
|
||||
context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
|
||||
}
|
||||
|
||||
@@ -215,7 +200,6 @@ pub(crate) fn codegen(
|
||||
config.emit_asm,
|
||||
config.emit_ir,
|
||||
&cgcx.output_filenames,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -291,8 +291,8 @@ fn target_cpu(&self, sess: &Session) -> String {
|
||||
target_cpu(sess).to_owned()
|
||||
}
|
||||
|
||||
fn codegen_crate(&self, tcx: TyCtxt<'_>, crate_info: &CrateInfo) -> Box<dyn Any> {
|
||||
Box::new(codegen_crate(self.clone(), tcx, crate_info))
|
||||
fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
|
||||
Box::new(codegen_crate(self.clone(), tcx))
|
||||
}
|
||||
|
||||
fn join_codegen(
|
||||
@@ -300,11 +300,12 @@ fn join_codegen(
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
sess: &Session,
|
||||
_outputs: &OutputFilenames,
|
||||
crate_info: &CrateInfo,
|
||||
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
ongoing_codegen
|
||||
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
|
||||
.expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
|
||||
.join(sess)
|
||||
.join(sess, crate_info)
|
||||
}
|
||||
|
||||
fn target_config(&self, sess: &Session) -> TargetConfig {
|
||||
|
||||
@@ -117,17 +117,13 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar
|
||||
tcx.sess.split_debuginfo(),
|
||||
tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
||||
mod_name,
|
||||
tcx.sess.invocation_temp.as_deref(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu(
|
||||
OutputType::Object,
|
||||
mod_name,
|
||||
tcx.sess.invocation_temp.as_deref(),
|
||||
));
|
||||
let output_obj_file =
|
||||
Some(tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, mod_name));
|
||||
let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file };
|
||||
|
||||
target_machine_factory(
|
||||
@@ -322,11 +318,7 @@ pub(crate) fn save_temp_bitcode(
|
||||
return;
|
||||
}
|
||||
let ext = format!("{name}.bc");
|
||||
let path = cgcx.output_filenames.temp_path_ext_for_cgu(
|
||||
&ext,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let path = cgcx.output_filenames.temp_path_ext_for_cgu(&ext, &module.name);
|
||||
write_bitcode_to_file(&module.module_llvm, &path)
|
||||
}
|
||||
|
||||
@@ -949,11 +941,8 @@ pub(crate) fn optimize(
|
||||
if let Some(thin_lto_buffer) = thin_lto_buffer {
|
||||
let thin_lto_buffer = thin_lto_buffer.unwrap();
|
||||
module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec());
|
||||
let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::ThinLinkBitcode,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let bc_summary_out =
|
||||
cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name);
|
||||
if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer
|
||||
&& let Some(thin_link_bitcode_filename) = bc_summary_out.file_name()
|
||||
{
|
||||
@@ -1008,16 +997,8 @@ pub(crate) fn codegen(
|
||||
// copy it to the .o file, and delete the bitcode if it wasn't
|
||||
// otherwise requested.
|
||||
|
||||
let bc_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Bitcode,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let obj_out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Object,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name);
|
||||
let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name);
|
||||
|
||||
if config.bitcode_needed() {
|
||||
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
||||
@@ -1055,11 +1036,8 @@ pub(crate) fn codegen(
|
||||
if config.emit_ir {
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
|
||||
let out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::LlvmAssembly,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let out =
|
||||
cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name);
|
||||
let out_c = path_to_c_string(&out);
|
||||
|
||||
extern "C" fn demangle_callback(
|
||||
@@ -1103,11 +1081,7 @@ extern "C" fn demangle_callback(
|
||||
if config.emit_asm {
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
|
||||
let path = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Assembly,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name);
|
||||
|
||||
// We can't use the same module for asm and object code output,
|
||||
// because that triggers various errors like invalid IR or broken
|
||||
@@ -1136,9 +1110,7 @@ extern "C" fn demangle_callback(
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
|
||||
|
||||
let dwo_out = cgcx
|
||||
.output_filenames
|
||||
.temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref());
|
||||
let dwo_out = cgcx.output_filenames.temp_path_dwo_for_cgu(&module.name);
|
||||
let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) {
|
||||
// Don't change how DWARF is emitted when disabled.
|
||||
(SplitDebuginfo::Off, _) => None,
|
||||
@@ -1203,7 +1175,6 @@ extern "C" fn demangle_callback(
|
||||
config.emit_asm,
|
||||
config.emit_ir,
|
||||
&cgcx.output_filenames,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -903,7 +903,6 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
|
||||
tcx.sess.split_debuginfo(),
|
||||
tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
||||
codegen_unit_name,
|
||||
tcx.sess.invocation_temp.as_deref(),
|
||||
) {
|
||||
// We get a path relative to the working directory from split_dwarf_path
|
||||
Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f))
|
||||
|
||||
@@ -333,8 +333,8 @@ fn target_cpu(&self, sess: &Session) -> String {
|
||||
crate::llvm_util::target_cpu(sess).to_string()
|
||||
}
|
||||
|
||||
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box<dyn Any> {
|
||||
Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info))
|
||||
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
|
||||
Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx))
|
||||
}
|
||||
|
||||
fn join_codegen(
|
||||
@@ -342,11 +342,12 @@ fn join_codegen(
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
sess: &Session,
|
||||
outputs: &OutputFilenames,
|
||||
crate_info: &CrateInfo,
|
||||
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
let (compiled_modules, work_products) = ongoing_codegen
|
||||
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
|
||||
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
|
||||
.join(sess);
|
||||
.join(sess, crate_info);
|
||||
|
||||
if sess.opts.unstable_opts.llvm_time_trace {
|
||||
sess.time("llvm_dump_timing_file", || {
|
||||
|
||||
@@ -112,12 +112,7 @@ pub fn link_binary(
|
||||
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
|
||||
|
||||
let crate_name = format!("{}", crate_info.local_crate_name);
|
||||
let out_filename = output.file_for_writing(
|
||||
outputs,
|
||||
OutputType::Exe,
|
||||
&crate_name,
|
||||
sess.invocation_temp.as_deref(),
|
||||
);
|
||||
let out_filename = output.file_for_writing(outputs, OutputType::Exe, &crate_name);
|
||||
match crate_type {
|
||||
CrateType::Rlib => {
|
||||
let _timer = sess.timer("link_rlib");
|
||||
|
||||
@@ -85,7 +85,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn exported_symbols_for_lto(
|
||||
pub(crate) fn exported_symbols_for_lto(
|
||||
tcx: TyCtxt<'_>,
|
||||
each_linked_rlib_for_lto: &[CrateNum],
|
||||
) -> Vec<String> {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
use rustc_target::spec::{MergeFunctions, SanitizerSet};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::back::link::{self, ensure_removed};
|
||||
use crate::back::link::ensure_removed;
|
||||
use crate::back::lto::{self, SerializedModule, check_lto_allowed};
|
||||
use crate::errors::ErrorCreatingRemarkDir;
|
||||
use crate::traits::*;
|
||||
@@ -136,7 +136,8 @@ macro_rules! if_regular {
|
||||
let emit_obj = if !should_emit_obj {
|
||||
EmitObj::None
|
||||
} else if sess.target.obj_is_bitcode
|
||||
|| (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins)
|
||||
|| (sess.opts.cg.linker_plugin_lto.enabled()
|
||||
&& (!no_builtins || tcx.sess.is_sanitizer_cfi_enabled()))
|
||||
{
|
||||
// This case is selected if the target uses objects as bitcode, or
|
||||
// if linker plugin LTO is enabled. In the linker plugin LTO case
|
||||
@@ -144,14 +145,23 @@ macro_rules! if_regular {
|
||||
// and convert it to object code. This may be done by either the
|
||||
// native linker or rustc itself.
|
||||
//
|
||||
// Note, however, that the linker-plugin-lto requested here is
|
||||
// explicitly ignored for `#![no_builtins]` crates. These crates are
|
||||
// specifically ignored by rustc's LTO passes and wouldn't work if
|
||||
// loaded into the linker. These crates define symbols that LLVM
|
||||
// lowers intrinsics to, and these symbol dependencies aren't known
|
||||
// until after codegen. As a result any crate marked
|
||||
// `#![no_builtins]` is assumed to not participate in LTO and
|
||||
// instead goes on to generate object code.
|
||||
// By default this branch is skipped for `#![no_builtins]` crates so
|
||||
// they emit native object files (machine code), not LLVM bitcode
|
||||
// objects for the linker (see rust-lang/rust#146133).
|
||||
//
|
||||
// However, when LLVM CFI is enabled (`-Zsanitizer=cfi`), this
|
||||
// breaks LLVM's expected pipeline: LLVM emits `llvm.type.test`
|
||||
// intrinsics and related metadata that must be lowered by LLVM's
|
||||
// `LowerTypeTests` pass before instruction selection during
|
||||
// link-time LTO. Otherwise, `llvm.type.test` intrinsics and related
|
||||
// metadata are not lowered by LLVM's `LowerTypeTests` pass before
|
||||
// reaching the target backend, and LLVM may abort during codegen
|
||||
// (for example in SelectionDAG type legalization) (see
|
||||
// rust-lang/rust#142284).
|
||||
//
|
||||
// Therefore, with `-Clinker-plugin-lto` and `-Zsanitizer=cfi`, a
|
||||
// `#![no_builtins]` crate must still use rustc's `EmitObj::Bitcode`
|
||||
// path (and emit LLVM bitcode in the `.o` for linker-based LTO).
|
||||
EmitObj::Bitcode
|
||||
} else if need_bitcode_in_object(tcx) || sess.target.requires_lto {
|
||||
EmitObj::ObjectCode(BitcodeSection::Full)
|
||||
@@ -285,17 +295,13 @@ pub fn new(cgcx: &CodegenContext, module_name: &str) -> TargetMachineFactoryConf
|
||||
cgcx.split_debuginfo,
|
||||
cgcx.split_dwarf_kind,
|
||||
module_name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Object,
|
||||
module_name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
));
|
||||
let output_obj_file =
|
||||
Some(cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, module_name));
|
||||
TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }
|
||||
}
|
||||
}
|
||||
@@ -322,7 +328,6 @@ pub struct CodegenContext {
|
||||
pub time_trace: bool,
|
||||
pub crate_types: Vec<CrateType>,
|
||||
pub output_filenames: Arc<OutputFilenames>,
|
||||
pub invocation_temp: Option<String>,
|
||||
pub module_config: Arc<ModuleConfig>,
|
||||
pub opt_level: OptLevel,
|
||||
pub backend_features: Vec<String>,
|
||||
@@ -389,18 +394,8 @@ fn generate_thin_lto_work<B: WriteBackendMethods>(
|
||||
|
||||
enum MaybeLtoModules<B: WriteBackendMethods> {
|
||||
NoLto(CompiledModules),
|
||||
FatLto {
|
||||
cgcx: CodegenContext,
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_file_for_lto: Vec<PathBuf>,
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
},
|
||||
ThinLto {
|
||||
cgcx: CodegenContext,
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_file_for_lto: Vec<PathBuf>,
|
||||
needs_thin_lto: Vec<ThinLtoInput<B>>,
|
||||
},
|
||||
FatLto { cgcx: CodegenContext, needs_fat_lto: Vec<FatLtoInput<B>> },
|
||||
ThinLto { cgcx: CodegenContext, needs_thin_lto: Vec<ThinLtoInput<B>> },
|
||||
}
|
||||
|
||||
fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool {
|
||||
@@ -424,7 +419,6 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool {
|
||||
pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
|
||||
backend: B,
|
||||
tcx: TyCtxt<'_>,
|
||||
crate_info: &CrateInfo,
|
||||
allocator_module: Option<ModuleCodegen<B::Module>>,
|
||||
) -> OngoingCodegen<B> {
|
||||
let (coordinator_send, coordinator_receive) = channel();
|
||||
@@ -440,7 +434,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
|
||||
let coordinator_thread = start_executing_work(
|
||||
backend.clone(),
|
||||
tcx,
|
||||
crate_info,
|
||||
shared_emitter,
|
||||
codegen_worker_send,
|
||||
coordinator_receive,
|
||||
@@ -529,11 +522,7 @@ pub fn produce_final_output_artifacts(
|
||||
if let [module] = &compiled_modules.modules[..] {
|
||||
// 1) Only one codegen unit. In this case it's no difficulty
|
||||
// to copy `foo.0.x` to `foo.x`.
|
||||
let path = crate_output.temp_path_for_cgu(
|
||||
output_type,
|
||||
&module.name,
|
||||
sess.invocation_temp.as_deref(),
|
||||
);
|
||||
let path = crate_output.temp_path_for_cgu(output_type, &module.name);
|
||||
let output = crate_output.path(output_type);
|
||||
if !output_type.is_text_output() && output.is_tty() {
|
||||
sess.dcx()
|
||||
@@ -912,12 +901,7 @@ fn execute_copy_from_cache_work_item(
|
||||
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
|
||||
let dwarf_obj_out = cgcx
|
||||
.output_filenames
|
||||
.split_dwarf_path(
|
||||
cgcx.split_debuginfo,
|
||||
cgcx.split_dwarf_kind,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
)
|
||||
.split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, &module.name)
|
||||
.expect(
|
||||
"saved dwarf object in work product but `split_dwarf_path` returned `None`",
|
||||
);
|
||||
@@ -927,11 +911,7 @@ fn execute_copy_from_cache_work_item(
|
||||
let mut load_from_incr_cache = |perform, output_type: OutputType| {
|
||||
if perform {
|
||||
let saved_file = module.source.saved_files.get(output_type.extension())?;
|
||||
let output_path = cgcx.output_filenames.temp_path_for_cgu(
|
||||
output_type,
|
||||
&module.name,
|
||||
cgcx.invocation_temp.as_deref(),
|
||||
);
|
||||
let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name);
|
||||
load_from_incr_comp_dir(output_path, &saved_file)
|
||||
} else {
|
||||
None
|
||||
@@ -992,8 +972,8 @@ fn do_thin_lto<B: WriteBackendMethods>(
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<B>,
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_for_lto: Vec<PathBuf>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
needs_thin_lto: Vec<ThinLtoInput<B>>,
|
||||
) -> Vec<CompiledModule> {
|
||||
let _timer = prof.verbose_generic_activity("LLVM_thinlto");
|
||||
@@ -1231,7 +1211,6 @@ enum MainThreadState {
|
||||
fn start_executing_work<B: ExtraBackendMethods>(
|
||||
backend: B,
|
||||
tcx: TyCtxt<'_>,
|
||||
crate_info: &CrateInfo,
|
||||
shared_emitter: SharedEmitter,
|
||||
codegen_worker_send: Sender<CguMessage>,
|
||||
coordinator_receive: Receiver<Message<B>>,
|
||||
@@ -1243,22 +1222,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
let sess = tcx.sess;
|
||||
let prof = sess.prof.clone();
|
||||
|
||||
let mut each_linked_rlib_for_lto = Vec::new();
|
||||
let mut each_linked_rlib_file_for_lto = Vec::new();
|
||||
if sess.lto() != Lto::No && sess.lto() != Lto::ThinLocal {
|
||||
drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
|
||||
if link::ignored_for_lto(sess, crate_info, cnum) {
|
||||
return;
|
||||
}
|
||||
|
||||
each_linked_rlib_for_lto.push(cnum);
|
||||
each_linked_rlib_file_for_lto.push(path.to_path_buf());
|
||||
}));
|
||||
}
|
||||
|
||||
// Compute the set of symbols we need to retain when doing LTO (if we need to)
|
||||
// Compute the set of symbols we need to retain when doing thin local LTO (if we need to)
|
||||
let exported_symbols_for_lto =
|
||||
Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto));
|
||||
if sess.lto() == Lto::ThinLocal { lto::exported_symbols_for_lto(tcx, &[]) } else { vec![] };
|
||||
|
||||
// First up, convert our jobserver into a helper thread so we can use normal
|
||||
// mpsc channels to manage our messages and such.
|
||||
@@ -1313,7 +1279,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
|
||||
parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend,
|
||||
pointer_size: tcx.data_layout.pointer_size(),
|
||||
invocation_temp: sess.invocation_temp.clone(),
|
||||
};
|
||||
|
||||
// This is the "main loop" of parallel work happening for parallel codegen.
|
||||
@@ -1757,12 +1722,7 @@ enum CodegenState {
|
||||
needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, bitcode_path })
|
||||
}
|
||||
|
||||
return Ok(MaybeLtoModules::FatLto {
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
});
|
||||
return Ok(MaybeLtoModules::FatLto { cgcx, needs_fat_lto });
|
||||
} else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() {
|
||||
assert!(compiled_modules.is_empty());
|
||||
assert!(needs_fat_lto.is_empty());
|
||||
@@ -1777,8 +1737,8 @@ enum CodegenState {
|
||||
&prof,
|
||||
shared_emitter.clone(),
|
||||
tm_factory,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
&exported_symbols_for_lto,
|
||||
&[],
|
||||
needs_thin_lto,
|
||||
));
|
||||
} else {
|
||||
@@ -1790,12 +1750,7 @@ enum CodegenState {
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(MaybeLtoModules::ThinLto {
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
});
|
||||
return Ok(MaybeLtoModules::ThinLto { cgcx, needs_thin_lto });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2139,7 +2094,11 @@ pub struct OngoingCodegen<B: WriteBackendMethods> {
|
||||
}
|
||||
|
||||
impl<B: WriteBackendMethods> OngoingCodegen<B> {
|
||||
pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
pub fn join(
|
||||
self,
|
||||
sess: &Session,
|
||||
crate_info: &CrateInfo,
|
||||
) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
self.shared_emitter_main.check(sess, true);
|
||||
|
||||
let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() {
|
||||
@@ -2163,12 +2122,7 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId,
|
||||
drop(shared_emitter);
|
||||
compiled_modules
|
||||
}
|
||||
MaybeLtoModules::FatLto {
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
} => {
|
||||
MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => {
|
||||
let tm_factory = self.backend.target_machine_factory(
|
||||
sess,
|
||||
cgcx.opt_level,
|
||||
@@ -2181,19 +2135,14 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId,
|
||||
&cgcx,
|
||||
shared_emitter,
|
||||
tm_factory,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_file_for_lto,
|
||||
&crate_info.exported_symbols_for_lto,
|
||||
&crate_info.each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
)],
|
||||
allocator_module: None,
|
||||
}
|
||||
}
|
||||
MaybeLtoModules::ThinLto {
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
} => {
|
||||
MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => {
|
||||
let tm_factory = self.backend.target_machine_factory(
|
||||
sess,
|
||||
cgcx.opt_level,
|
||||
@@ -2206,8 +2155,8 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap<WorkProductId,
|
||||
&sess.prof,
|
||||
shared_emitter,
|
||||
tm_factory,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
&crate_info.exported_symbols_for_lto,
|
||||
&crate_info.each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
),
|
||||
allocator_module: None,
|
||||
|
||||
@@ -686,11 +686,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec<Allo
|
||||
methods
|
||||
}
|
||||
|
||||
pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||
backend: B,
|
||||
tcx: TyCtxt<'_>,
|
||||
crate_info: &CrateInfo,
|
||||
) -> OngoingCodegen<B> {
|
||||
pub fn codegen_crate<B: ExtraBackendMethods>(backend: B, tcx: TyCtxt<'_>) -> OngoingCodegen<B> {
|
||||
if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() {
|
||||
// The target has no default cpu, but none is set explicitly
|
||||
tcx.dcx().emit_fatal(errors::CpuRequired);
|
||||
@@ -734,7 +730,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||
None
|
||||
};
|
||||
|
||||
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, crate_info, allocator_module);
|
||||
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module);
|
||||
|
||||
// For better throughput during parallel processing by LLVM, we used to sort
|
||||
// CGUs largest to smallest. This would lead to better thread utilization
|
||||
@@ -959,6 +955,8 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
|
||||
natvis_debugger_visualizers: Default::default(),
|
||||
lint_levels: CodegenLintLevels::from_tcx(tcx),
|
||||
metadata_symbol: exported_symbols::metadata_symbol_name(tcx),
|
||||
each_linked_rlib_file_for_lto: Default::default(),
|
||||
exported_symbols_for_lto: Default::default(),
|
||||
};
|
||||
|
||||
info.native_libraries.reserve(n_crates);
|
||||
@@ -1044,6 +1042,25 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
|
||||
});
|
||||
}
|
||||
|
||||
let mut each_linked_rlib_for_lto = Vec::new();
|
||||
let mut each_linked_rlib_file_for_lto = Vec::new();
|
||||
if tcx.sess.lto() != config::Lto::No && tcx.sess.lto() != config::Lto::ThinLocal {
|
||||
drop(crate::back::link::each_linked_rlib(&info, None, &mut |cnum, path| {
|
||||
if crate::back::link::ignored_for_lto(tcx.sess, &info, cnum) {
|
||||
return;
|
||||
}
|
||||
|
||||
each_linked_rlib_for_lto.push(cnum);
|
||||
each_linked_rlib_file_for_lto.push(path.to_path_buf());
|
||||
}));
|
||||
}
|
||||
info.each_linked_rlib_file_for_lto = each_linked_rlib_file_for_lto;
|
||||
|
||||
// FIXME move to -Zlink-only half such that each_linked_rlib_file_for_lto can be moved there too
|
||||
// Compute the set of symbols we need to retain when doing LTO (if we need to)
|
||||
info.exported_symbols_for_lto =
|
||||
crate::back::lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto);
|
||||
|
||||
let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type {
|
||||
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => {
|
||||
// These are crate types for which we invoke the linker and can embed
|
||||
|
||||
@@ -95,19 +95,14 @@ pub fn into_compiled_module(
|
||||
emit_asm: bool,
|
||||
emit_ir: bool,
|
||||
outputs: &OutputFilenames,
|
||||
invocation_temp: Option<&str>,
|
||||
) -> CompiledModule {
|
||||
let object = emit_obj
|
||||
.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp));
|
||||
let dwarf_object =
|
||||
emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp));
|
||||
let bytecode = emit_bc
|
||||
.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp));
|
||||
let assembly = emit_asm
|
||||
.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp));
|
||||
let llvm_ir = emit_ir.then(|| {
|
||||
outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp)
|
||||
});
|
||||
let object = emit_obj.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name));
|
||||
let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name));
|
||||
let bytecode = emit_bc.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name));
|
||||
let assembly =
|
||||
emit_asm.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name));
|
||||
let llvm_ir =
|
||||
emit_ir.then(|| outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name));
|
||||
|
||||
CompiledModule {
|
||||
name: self.name,
|
||||
@@ -230,6 +225,8 @@ pub struct CrateInfo {
|
||||
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
|
||||
pub lint_levels: CodegenLintLevels,
|
||||
pub metadata_symbol: String,
|
||||
pub each_linked_rlib_file_for_lto: Vec<PathBuf>,
|
||||
pub exported_symbols_for_lto: Vec<String>,
|
||||
}
|
||||
|
||||
/// Target-specific options that get set in `cfg(...)`.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_data_structures::stable_hasher::ToStableHashKey;
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
|
||||
use rustc_macros::{Decodable, Encodable, StableHash};
|
||||
@@ -712,15 +711,6 @@ fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStableHashKey for Namespace {
|
||||
type KeyType = Namespace;
|
||||
|
||||
#[inline]
|
||||
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> Namespace {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
/// Just a helper ‒ separate structure for each namespace.
|
||||
#[derive(Copy, Clone, Default, Debug, StableHash)]
|
||||
pub struct PerNS<T> {
|
||||
|
||||
@@ -1,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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -1305,8 +1305,6 @@ pub(crate) fn start_codegen<'tcx>(
|
||||
|
||||
let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx);
|
||||
|
||||
let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess));
|
||||
|
||||
let codegen = tcx.sess.time("codegen_crate", || {
|
||||
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
|
||||
// Skip crate items and just output metadata in -Z no-codegen mode.
|
||||
@@ -1315,7 +1313,7 @@ pub(crate) fn start_codegen<'tcx>(
|
||||
// Linker::link will skip join_codegen in case of a CodegenResults Any value.
|
||||
Box::new(CompiledModules { modules: vec![], allocator_module: None })
|
||||
} else {
|
||||
codegen_backend.codegen_crate(tcx, &crate_info)
|
||||
codegen_backend.codegen_crate(tcx)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1327,6 +1325,8 @@ pub(crate) fn start_codegen<'tcx>(
|
||||
tcx.sess.code_stats.print_type_sizes();
|
||||
}
|
||||
|
||||
let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess));
|
||||
|
||||
(codegen, crate_info, metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,9 +53,12 @@ pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) {
|
||||
// This was a check only build
|
||||
Ok(compiled_modules) => (*compiled_modules, IndexMap::default()),
|
||||
|
||||
Err(ongoing_codegen) => {
|
||||
codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames)
|
||||
}
|
||||
Err(ongoing_codegen) => codegen_backend.join_codegen(
|
||||
ongoing_codegen,
|
||||
sess,
|
||||
&self.output_filenames,
|
||||
&self.crate_info,
|
||||
),
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -138,17 +138,16 @@
|
||||
USES_POWER_ALIGNMENT
|
||||
]);
|
||||
|
||||
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
|
||||
#[inline]
|
||||
fn get_type_from_field<'tcx>(
|
||||
/// A common pattern in this lint is to attempt normalize_erasing_regions,
|
||||
/// but keep the original type if it were to fail.
|
||||
/// This may or may not be supported in the logic behind the `Unnormalized` wrapper,
|
||||
/// (FIXME?)
|
||||
/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be.
|
||||
fn maybe_normalize_erasing_regions<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
field: &ty::FieldDef,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
value: Unnormalized<'tcx, Ty<'tcx>>,
|
||||
) -> Ty<'tcx> {
|
||||
let field_ty = field.ty(cx.tcx, args);
|
||||
cx.tcx
|
||||
.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty))
|
||||
.unwrap_or(field_ty)
|
||||
cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip())
|
||||
}
|
||||
|
||||
/// Check a variant of a non-exhaustive enum for improper ctypes
|
||||
@@ -257,12 +256,22 @@ fn check_struct_for_power_alignment<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Annotates whether we are in the context of an item *defined* in rust
|
||||
/// and exposed to an FFI boundary,
|
||||
/// or the context of an item from elsewhere, whose interface is re-*declared* in rust.
|
||||
#[derive(Clone, Copy)]
|
||||
enum CItemKind {
|
||||
Declaration,
|
||||
Definition,
|
||||
}
|
||||
|
||||
/// Annotates whether we are in the context of a function's argument types or return type.
|
||||
#[derive(Clone, Copy)]
|
||||
enum FnPos {
|
||||
Arg,
|
||||
Ret,
|
||||
}
|
||||
|
||||
enum FfiResult<'tcx> {
|
||||
FfiSafe,
|
||||
FfiPhantom(Ty<'tcx>),
|
||||
@@ -286,8 +295,10 @@ enum IndirectionKind {
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// VisitorState flags that are linked with the root type's use.
|
||||
/// (These are the permanent part of the state, kept when visiting new Ty.)
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct VisitorState: u8 {
|
||||
struct RootUseFlags: u8 {
|
||||
/// For use in (externally-linked) static variables.
|
||||
const STATIC = 0b000001;
|
||||
/// For use in functions in general.
|
||||
@@ -302,7 +313,45 @@ struct VisitorState: u8 {
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitorState {
|
||||
/// Description of the relationship between current Ty and
|
||||
/// the type (or lack thereof) immediately containing it
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum OuterTyKind {
|
||||
None,
|
||||
/// A variant that should not exist,
|
||||
/// but is needed because we don't change the lint's behavior yet
|
||||
NoneThroughFnPtr,
|
||||
/// Placeholder for properties that will be used eventually
|
||||
Other,
|
||||
}
|
||||
|
||||
impl OuterTyKind {
|
||||
/// Computes the relationship by providing the containing Ty itself
|
||||
fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self {
|
||||
match ty.kind() {
|
||||
ty::FnPtr(..) => Self::NoneThroughFnPtr,
|
||||
ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::Adt(..)
|
||||
| ty::Tuple(..)
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_) => OuterTyKind::Other,
|
||||
_ => bug!("Unexpected outer type {ty:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
struct VisitorState {
|
||||
/// Flags describing both the overall context in which the current Ty is,
|
||||
/// linked to how the Visitor's original Ty was used.
|
||||
root_use_flags: RootUseFlags,
|
||||
/// Flags describing both the immediate context in which the current Ty is,
|
||||
/// linked to how it relates to its parent Ty (or lack thereof).
|
||||
outer_ty_kind: OuterTyKind,
|
||||
}
|
||||
|
||||
impl RootUseFlags {
|
||||
// The values that can be set.
|
||||
const STATIC_TY: Self = Self::STATIC;
|
||||
const ARGUMENT_TY_IN_DEFINITION: Self =
|
||||
@@ -317,86 +366,85 @@ impl VisitorState {
|
||||
const RETURN_TY_IN_FNPTR: Self =
|
||||
Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Get the proper visitor state for a given function's arguments.
|
||||
fn argument_from_fnmode(fn_mode: CItemKind) -> Self {
|
||||
match fn_mode {
|
||||
CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION,
|
||||
CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION,
|
||||
impl VisitorState {
|
||||
/// From an existing state, compute the state of any subtype of the current type.
|
||||
/// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.)
|
||||
fn next(&self, current_ty: Ty<'_>) -> Self {
|
||||
assert!(!matches!(current_ty.kind(), ty::FnPtr(..)));
|
||||
VisitorState {
|
||||
root_use_flags: self.root_use_flags,
|
||||
outer_ty_kind: OuterTyKind::from_ty(current_ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the proper visitor state for a given function's return type.
|
||||
fn return_from_fnmode(fn_mode: CItemKind) -> Self {
|
||||
match fn_mode {
|
||||
CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION,
|
||||
CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION,
|
||||
/// From an existing state, compute the state of any subtype of the current type.
|
||||
/// (Case where the current type is a function pointer,
|
||||
/// meaning we need to specify if the subtype is an argument or the return.)
|
||||
fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self {
|
||||
assert!(matches!(current_ty.kind(), ty::FnPtr(..)));
|
||||
VisitorState {
|
||||
root_use_flags: match fn_pos {
|
||||
FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR,
|
||||
FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR,
|
||||
},
|
||||
outer_ty_kind: OuterTyKind::from_ty(current_ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the proper visitor state for a given function's arguments or return type.
|
||||
fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self {
|
||||
let p_flags = match (fn_mode, fn_pos) {
|
||||
(CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION,
|
||||
(CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION,
|
||||
(CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION,
|
||||
(CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION,
|
||||
};
|
||||
VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None }
|
||||
}
|
||||
|
||||
/// Get the proper visitor state for a static variable's type
|
||||
fn static_entry_point() -> Self {
|
||||
VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None }
|
||||
}
|
||||
|
||||
/// Whether the type is used in a function.
|
||||
fn is_in_function(self) -> bool {
|
||||
let ret = self.contains(Self::FUNC);
|
||||
fn is_in_function(&self) -> bool {
|
||||
let ret = self.root_use_flags.contains(RootUseFlags::FUNC);
|
||||
if ret {
|
||||
debug_assert!(!self.contains(Self::STATIC));
|
||||
debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC));
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Whether the type is used (directly or not) in a function, in return position.
|
||||
fn is_in_function_return(self) -> bool {
|
||||
let ret = self.contains(Self::FN_RETURN);
|
||||
fn is_in_function_return(&self) -> bool {
|
||||
let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN);
|
||||
if ret {
|
||||
debug_assert!(self.is_in_function());
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Whether the type is used (directly or not) in a defined function.
|
||||
/// In other words, whether or not we allow non-FFI-safe types behind a C pointer,
|
||||
/// to be treated as an opaque type on the other side of the FFI boundary.
|
||||
fn is_in_defined_function(self) -> bool {
|
||||
self.contains(Self::DEFINED) && self.is_in_function()
|
||||
fn is_in_defined_function(&self) -> bool {
|
||||
self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function()
|
||||
}
|
||||
|
||||
/// Whether the type is used (directly or not) in a function pointer type.
|
||||
/// Here, we also allow non-FFI-safe types behind a C pointer,
|
||||
/// to be treated as an opaque type on the other side of the FFI boundary.
|
||||
fn is_in_fnptr(self) -> bool {
|
||||
self.contains(Self::THEORETICAL) && self.is_in_function()
|
||||
fn is_in_fnptr(&self) -> bool {
|
||||
self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function()
|
||||
}
|
||||
|
||||
/// Whether we can expect type parameters and co in a given type.
|
||||
fn can_expect_ty_params(self) -> bool {
|
||||
fn can_expect_ty_params(&self) -> bool {
|
||||
// rust-defined functions, as well as FnPtrs
|
||||
self.contains(Self::THEORETICAL) || self.is_in_defined_function()
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Data that summarises how an "outer type" surrounds its inner type(s)
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct OuterTyData: u8 {
|
||||
/// To show that there is no outer type, the current type is directly used by a `static`
|
||||
/// variable or a function/FnPtr
|
||||
const NO_OUTER_TY = 0b01;
|
||||
/// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically
|
||||
/// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake
|
||||
const NO_OUTER_TY_FNPTR = 0b10;
|
||||
}
|
||||
}
|
||||
|
||||
impl OuterTyData {
|
||||
/// Get the proper data for a given outer type.
|
||||
fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self {
|
||||
match ty.kind() {
|
||||
ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR,
|
||||
ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::Adt(..)
|
||||
| ty::Tuple(..)
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_) => Self::empty(),
|
||||
k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k),
|
||||
}
|
||||
self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self {
|
||||
Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() }
|
||||
fn new(
|
||||
cx: &'a LateContext<'tcx>,
|
||||
base_ty: Unnormalized<'tcx, Ty<'tcx>>,
|
||||
base_fn_mode: CItemKind,
|
||||
) -> Self {
|
||||
ImproperCTypesVisitor {
|
||||
cx,
|
||||
base_ty: maybe_normalize_erasing_regions(cx, base_ty),
|
||||
base_fn_mode,
|
||||
cache: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
|
||||
@@ -485,7 +542,7 @@ fn visit_indirection(
|
||||
{
|
||||
FfiSafe
|
||||
} else {
|
||||
self.visit_type(state, OuterTyData::from_ty(ty), inner_ty)
|
||||
self.visit_type(state.next(ty), inner_ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,8 +562,11 @@ fn visit_variant_fields(
|
||||
let transparent_with_all_zst_fields = if def.repr().transparent() {
|
||||
if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) {
|
||||
// Transparent newtypes have at most one non-ZST field which needs to be checked..
|
||||
let field_ty = get_type_from_field(self.cx, field, args);
|
||||
match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) {
|
||||
let field_ty = maybe_normalize_erasing_regions(
|
||||
self.cx,
|
||||
Unnormalized::new_wip(field.ty(self.cx.tcx, args)),
|
||||
);
|
||||
match self.visit_type(state.next(ty), field_ty) {
|
||||
FfiUnsafe { ty, .. } if ty.is_unit() => (),
|
||||
r => return r,
|
||||
}
|
||||
@@ -524,8 +584,11 @@ fn visit_variant_fields(
|
||||
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
|
||||
let mut all_phantom = !variant.fields.is_empty();
|
||||
for field in &variant.fields {
|
||||
let field_ty = get_type_from_field(self.cx, field, args);
|
||||
all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) {
|
||||
let field_ty = maybe_normalize_erasing_regions(
|
||||
self.cx,
|
||||
Unnormalized::new_wip(field.ty(self.cx.tcx, args)),
|
||||
);
|
||||
all_phantom &= match self.visit_type(state.next(ty), field_ty) {
|
||||
FfiSafe => false,
|
||||
// `()` fields are FFI-safe!
|
||||
FfiUnsafe { ty, .. } if ty.is_unit() => false,
|
||||
@@ -570,7 +633,7 @@ fn visit_struct_or_union(
|
||||
"consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct"
|
||||
))
|
||||
} else {
|
||||
// FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises
|
||||
// FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises
|
||||
Some(msg!(
|
||||
"consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union"
|
||||
))
|
||||
@@ -628,7 +691,7 @@ fn visit_enum(
|
||||
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() {
|
||||
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
|
||||
if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) {
|
||||
return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty);
|
||||
return self.visit_type(state.next(ty), inner_ty);
|
||||
}
|
||||
|
||||
return FfiUnsafe {
|
||||
@@ -660,12 +723,7 @@ fn visit_enum(
|
||||
|
||||
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
|
||||
/// representation which can be exported to C code).
|
||||
fn visit_type(
|
||||
&mut self,
|
||||
state: VisitorState,
|
||||
outer_ty: OuterTyData,
|
||||
ty: Ty<'tcx>,
|
||||
) -> FfiResult<'tcx> {
|
||||
fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
|
||||
use FfiResult::*;
|
||||
|
||||
let tcx = self.cx.tcx;
|
||||
@@ -709,7 +767,8 @@ fn visit_type(
|
||||
// Pattern types are just extra invariants on the type that you need to uphold,
|
||||
// but only the base type is relevant for being representable in FFI.
|
||||
// (note: this lint was written when pattern types could only be integers constrained to ranges)
|
||||
ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty),
|
||||
// (also note: the lack of ".next(ty)" on the state is on purpose)
|
||||
ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty),
|
||||
|
||||
// types which likely have a stable representation, if the target architecture defines those
|
||||
// note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64
|
||||
@@ -740,12 +799,14 @@ fn visit_type(
|
||||
},
|
||||
|
||||
ty::Tuple(tuple) => {
|
||||
// C functions can return void
|
||||
let empty_and_safe = tuple.is_empty()
|
||||
&& outer_ty.contains(OuterTyData::NO_OUTER_TY)
|
||||
&& state.is_in_function_return();
|
||||
|
||||
if empty_and_safe {
|
||||
if tuple.is_empty()
|
||||
&& state.is_in_function_return()
|
||||
&& matches!(
|
||||
state.outer_ty_kind,
|
||||
OuterTyKind::None | OuterTyKind::NoneThroughFnPtr
|
||||
)
|
||||
{
|
||||
// C functions can return void
|
||||
FfiSafe
|
||||
} else {
|
||||
FfiUnsafe {
|
||||
@@ -774,9 +835,8 @@ fn visit_type(
|
||||
|
||||
ty::Array(inner_ty, _) => {
|
||||
if state.is_in_function()
|
||||
&& outer_ty.contains(OuterTyData::NO_OUTER_TY)
|
||||
// FIXME(ctypes): VVV-this-VVV shouldn't be the case
|
||||
&& !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR)
|
||||
// FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr
|
||||
&& matches!(state.outer_ty_kind, OuterTyKind::None)
|
||||
{
|
||||
// C doesn't really support passing arrays by value - the only way to pass an array by value
|
||||
// is through a struct.
|
||||
@@ -788,7 +848,7 @@ fn visit_type(
|
||||
} else {
|
||||
// let's allow phantoms to go through,
|
||||
// since an array of 1-ZSTs is also a 1-ZST
|
||||
self.visit_type(state, OuterTyData::from_ty(ty), inner_ty)
|
||||
self.visit_type(state.next(ty), inner_ty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,19 +866,14 @@ fn visit_type(
|
||||
|
||||
let sig = tcx.instantiate_bound_regions_with_erased(sig);
|
||||
for arg in sig.inputs() {
|
||||
match self.visit_type(
|
||||
VisitorState::ARGUMENT_TY_IN_FNPTR,
|
||||
OuterTyData::from_ty(ty),
|
||||
*arg,
|
||||
) {
|
||||
match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) {
|
||||
FfiSafe => {}
|
||||
r => return r,
|
||||
}
|
||||
}
|
||||
|
||||
let ret_ty = sig.output();
|
||||
|
||||
self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty)
|
||||
self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty)
|
||||
}
|
||||
|
||||
ty::Foreign(..) => FfiSafe,
|
||||
@@ -886,17 +941,17 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
|
||||
let ty = self
|
||||
.cx
|
||||
.tcx
|
||||
.try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty))
|
||||
.unwrap_or(ty);
|
||||
fn check_type(
|
||||
&mut self,
|
||||
state: VisitorState,
|
||||
ty: Unnormalized<'tcx, Ty<'tcx>>,
|
||||
) -> FfiResult<'tcx> {
|
||||
let ty = maybe_normalize_erasing_regions(self.cx, ty);
|
||||
if let Some(res) = self.visit_for_opaque_ty(ty) {
|
||||
return res;
|
||||
}
|
||||
|
||||
self.visit_type(state, OuterTyData::NO_OUTER_TY, ty)
|
||||
self.visit_type(state, ty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +980,7 @@ fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
|
||||
self.spans.push(ty.span);
|
||||
}
|
||||
|
||||
hir::intravisit::walk_ty(self, ty)
|
||||
hir::intravisit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,6 +1004,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
|
||||
let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
|
||||
for (fn_ptr_ty, span) in all_types {
|
||||
let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty);
|
||||
let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
|
||||
// FIXME(ctypes): make a check_for_fnptr
|
||||
let ffi_res = visitor.check_type(state, fn_ptr_ty);
|
||||
@@ -970,12 +1026,12 @@ fn check_fn_for_external_abi_fnptr(
|
||||
let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
|
||||
|
||||
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
|
||||
let state = VisitorState::argument_from_fnmode(fn_mode);
|
||||
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg);
|
||||
self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode);
|
||||
}
|
||||
|
||||
if let hir::FnRetTy::Return(ret_hir) = decl.output {
|
||||
let state = VisitorState::return_from_fnmode(fn_mode);
|
||||
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret);
|
||||
self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode);
|
||||
}
|
||||
}
|
||||
@@ -998,9 +1054,9 @@ fn check_reprc_adt(
|
||||
}
|
||||
|
||||
fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
|
||||
let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip();
|
||||
let ty = cx.tcx.type_of(id).instantiate_identity();
|
||||
let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
|
||||
let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty);
|
||||
let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty);
|
||||
self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
|
||||
}
|
||||
|
||||
@@ -1016,16 +1072,18 @@ fn check_foreign_fn(
|
||||
let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
|
||||
|
||||
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
|
||||
let state = VisitorState::argument_from_fnmode(fn_mode);
|
||||
let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
|
||||
let ffi_res = visitor.check_type(state, *input_ty);
|
||||
let input_ty = Unnormalized::new_wip(*input_ty);
|
||||
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg);
|
||||
let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode);
|
||||
let ffi_res = visitor.check_type(state, input_ty);
|
||||
self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode);
|
||||
}
|
||||
|
||||
if let hir::FnRetTy::Return(ret_hir) = decl.output {
|
||||
let state = VisitorState::return_from_fnmode(fn_mode);
|
||||
let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
|
||||
let ffi_res = visitor.check_type(state, sig.output());
|
||||
let output_ty = Unnormalized::new_wip(sig.output());
|
||||
let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret);
|
||||
let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode);
|
||||
let ffi_res = visitor.check_type(state, output_ty);
|
||||
self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode);
|
||||
}
|
||||
}
|
||||
@@ -1124,7 +1182,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
| hir::ItemKind::TyAlias(_, _, ty) => {
|
||||
self.check_type_for_external_abi_fnptr(
|
||||
cx,
|
||||
VisitorState::STATIC_TY,
|
||||
VisitorState::static_entry_point(),
|
||||
ty,
|
||||
cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(),
|
||||
CItemKind::Definition,
|
||||
@@ -1158,7 +1216,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
|
||||
self.check_type_for_external_abi_fnptr(
|
||||
cx,
|
||||
VisitorState::STATIC_TY,
|
||||
VisitorState::static_entry_point(),
|
||||
field.ty,
|
||||
cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(),
|
||||
CItemKind::Definition,
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::stable_hasher::{
|
||||
StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey,
|
||||
StableCompare, StableHash, StableHashCtxt, StableHasher,
|
||||
};
|
||||
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
|
||||
use rustc_hir_id::{HirId, ItemLocalId};
|
||||
use rustc_hir_id::HirId;
|
||||
use rustc_macros::{Decodable, Encodable, StableHash};
|
||||
use rustc_span::def_id::DefPathHash;
|
||||
pub use rustc_span::edition::Edition;
|
||||
use rustc_span::{AttrId, Ident, Symbol, sym};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -154,23 +153,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStableHashKey for LintExpectationId {
|
||||
type KeyType = (DefPathHash, ItemLocalId, u16, u16);
|
||||
|
||||
#[inline]
|
||||
fn to_stable_hash_key<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx) -> Self::KeyType {
|
||||
match self {
|
||||
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
|
||||
let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx);
|
||||
(def_path_hash, lint_idx, *attr_index, *lint_index)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("StableHash should only be called for a filled `LintExpectationId`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Setting for how to handle a lint.
|
||||
///
|
||||
/// See: <https://doc.rust-lang.org/rustc/lints/levels.html>
|
||||
@@ -623,15 +605,6 @@ fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHas
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStableHashKey for LintId {
|
||||
type KeyType = &'static str;
|
||||
|
||||
#[inline]
|
||||
fn to_stable_hash_key<Hcx>(&self, _: &mut Hcx) -> &'static str {
|
||||
self.lint_name_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl StableCompare for LintId {
|
||||
const CAN_USE_UNSTABLE_SORT: bool = true;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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,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",
|
||||
]
|
||||
+7
@@ -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" }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
include!("../shared_build_rs.rs");
|
||||
|
||||
fn main() {
|
||||
build_foo_static_lib(&[]);
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
int
|
||||
do_twice(int (*fn)(int), int arg)
|
||||
{
|
||||
return fn(arg) + fn(arg);
|
||||
}
|
||||
+36
@@ -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);
|
||||
}
|
||||
+7
@@ -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" }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
include!("../shared_build_rs.rs");
|
||||
|
||||
fn main() {
|
||||
build_foo_static_lib(&[]);
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
+67
@@ -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));
|
||||
}
|
||||
}
|
||||
+7
@@ -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"
|
||||
+10
@@ -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"
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
include!("../shared_build_rs.rs");
|
||||
|
||||
fn main() {
|
||||
build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]);
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
int
|
||||
do_twice(int (*fn)(int), int arg)
|
||||
{
|
||||
return fn(arg) + fn(arg);
|
||||
}
|
||||
+34
@@ -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);
|
||||
}
|
||||
+7
@@ -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"
|
||||
+10
@@ -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"
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
include!("../shared_build_rs.rs");
|
||||
|
||||
fn main() {
|
||||
build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]);
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
+89
@@ -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);
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "indirect-arity-mismatch-abort"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
+30
@@ -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);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "indirect-pointee-type-mismatch-abort"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
+32
@@ -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);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "indirect-return-type-mismatch-abort"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
+30
@@ -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);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "indirect-type-mismatch-abort"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
+30
@@ -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);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "indirect-type-qualifier-mismatch-abort"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
+31
@@ -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);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "invalid-branch-target-abort"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
+50
@@ -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");
|
||||
}
|
||||
+13
@@ -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]
|
||||
+96
@@ -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
-1
@@ -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() {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user