Merge ref 'b3cfb8faf8' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh. Upstream ref:b3cfb8faf8Filtered ref: 8995aa7743caf019203bc853f27af6006705ae30 Upstream diff:9385c64c95...b3cfb8faf8This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
commit
7c5fdb072e
501 changed files with 9306 additions and 2893 deletions
|
|
@ -5152,9 +5152,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "stringdex"
|
||||
version = "0.0.1-alpha4"
|
||||
version = "0.0.1-alpha9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
|
||||
checksum = "7081029913fd7d591c0112182aba8c98ae886b4f12edb208130496cd17dc3c15"
|
||||
dependencies = [
|
||||
"stacker",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -856,6 +856,9 @@
|
|||
# as libstd features, this option can also be used to configure features such as optimize_for_size.
|
||||
#rust.std-features = ["panic_unwind"]
|
||||
|
||||
# Trigger a `DebugBreak` after an internal compiler error during bootstrap on Windows
|
||||
#rust.break-on-ice = true
|
||||
|
||||
# =============================================================================
|
||||
# Distribution options
|
||||
#
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ pub enum CommentKind {
|
|||
Block,
|
||||
}
|
||||
|
||||
// This type must not implement `Hash` due to the unusual `PartialEq` impl below.
|
||||
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum InvisibleOrigin {
|
||||
// From the expansion of a metavariable in a declarative macro.
|
||||
MetaVar(MetaVarKind),
|
||||
|
|
@ -45,20 +44,6 @@ impl InvisibleOrigin {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InvisibleOrigin {
|
||||
#[inline]
|
||||
fn eq(&self, _other: &InvisibleOrigin) -> bool {
|
||||
// When we had AST-based nonterminals we couldn't compare them, and the
|
||||
// old `Nonterminal` type had an `eq` that always returned false,
|
||||
// resulting in this restriction:
|
||||
// https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment
|
||||
// This `eq` emulates that behaviour. We could consider lifting this
|
||||
// restriction now but there are still cases involving invisible
|
||||
// delimiters that make it harder than it first appears.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Annoyingly similar to `NonterminalKind`, but the slight differences are important.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
|
||||
pub enum MetaVarKind {
|
||||
|
|
@ -142,7 +127,8 @@ impl Delimiter {
|
|||
}
|
||||
}
|
||||
|
||||
// This exists because `InvisibleOrigin`s should be compared. It is only used for assertions.
|
||||
// This exists because `InvisibleOrigin`s should not be compared. It is only used for
|
||||
// assertions.
|
||||
pub fn eq_ignoring_invisible_origin(&self, other: &Delimiter) -> bool {
|
||||
match (self, other) {
|
||||
(Delimiter::Parenthesis, Delimiter::Parenthesis) => true,
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ attr_parsing_unrecognized_repr_hint =
|
|||
attr_parsing_unstable_cfg_target_compact =
|
||||
compact `cfg(target(..))` is experimental and subject to change
|
||||
|
||||
attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable
|
||||
attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable
|
||||
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
|
||||
|
||||
attr_parsing_unsupported_literal_cfg_boolean =
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ use cranelift_object::{ObjectBuilder, ObjectModule};
|
|||
use rustc_codegen_ssa::assert_module_sources::CguReuse;
|
||||
use rustc_codegen_ssa::back::link::ensure_removed;
|
||||
use rustc_codegen_ssa::base::determine_cgu_reuse;
|
||||
use rustc_codegen_ssa::{
|
||||
CodegenResults, CompiledModule, CrateInfo, ModuleKind, errors as ssa_errors,
|
||||
};
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, errors as ssa_errors};
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
|
||||
|
|
@ -363,7 +361,6 @@ fn emit_cgu(
|
|||
invocation_temp,
|
||||
prof,
|
||||
product.object,
|
||||
ModuleKind::Regular,
|
||||
name.clone(),
|
||||
producer,
|
||||
)?;
|
||||
|
|
@ -372,7 +369,6 @@ fn emit_cgu(
|
|||
module_regular,
|
||||
module_global_asm: global_asm_object_file.map(|global_asm_object_file| CompiledModule {
|
||||
name: format!("{name}.asm"),
|
||||
kind: ModuleKind::Regular,
|
||||
object: Some(global_asm_object_file),
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
|
|
@ -389,7 +385,6 @@ fn emit_module(
|
|||
invocation_temp: Option<&str>,
|
||||
prof: &SelfProfilerRef,
|
||||
mut object: cranelift_object::object::write::Object<'_>,
|
||||
kind: ModuleKind,
|
||||
name: String,
|
||||
producer_str: &str,
|
||||
) -> Result<CompiledModule, String> {
|
||||
|
|
@ -430,7 +425,6 @@ fn emit_module(
|
|||
|
||||
Ok(CompiledModule {
|
||||
name,
|
||||
kind,
|
||||
object: Some(tmp_file),
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
|
|
@ -485,7 +479,6 @@ fn reuse_workproduct_for_cgu(
|
|||
Ok(ModuleCodegenResult {
|
||||
module_regular: CompiledModule {
|
||||
name: cgu.name().to_string(),
|
||||
kind: ModuleKind::Regular,
|
||||
object: Some(obj_out_regular),
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
|
|
@ -495,7 +488,6 @@ fn reuse_workproduct_for_cgu(
|
|||
},
|
||||
module_global_asm: source_file_global_asm.map(|source_file| CompiledModule {
|
||||
name: cgu.name().to_string(),
|
||||
kind: ModuleKind::Regular,
|
||||
object: Some(obj_out_global_asm),
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
|
|
@ -651,7 +643,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> {
|
|||
tcx.sess.invocation_temp.as_deref(),
|
||||
&tcx.sess.prof,
|
||||
product.object,
|
||||
ModuleKind::Allocator,
|
||||
"allocator_shim".to_owned(),
|
||||
&crate::debuginfo::producer(tcx.sess),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ fn fat_lto(
|
|||
let path = tmp_path.path().to_path_buf().join(&module.name);
|
||||
let path = path.to_str().expect("path");
|
||||
let context = &module.module_llvm.context;
|
||||
let config = cgcx.config(module.kind);
|
||||
let config = &cgcx.module_config;
|
||||
// NOTE: we need to set the optimization level here in order for LTO to do its job.
|
||||
context.set_optimization_level(to_gcc_opt_level(config.opt_level));
|
||||
context.add_command_line_option("-flto=auto");
|
||||
|
|
|
|||
|
|
@ -29,8 +29,18 @@ pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[
|
|||
}
|
||||
|
||||
/// Get LLVM attribute for the provided inline heuristic.
|
||||
#[inline]
|
||||
fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> {
|
||||
pub(crate) fn inline_attr<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
) -> Option<&'ll Attribute> {
|
||||
// `optnone` requires `noinline`
|
||||
let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
|
||||
let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
|
||||
(_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
|
||||
(InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint,
|
||||
(inline, _) => inline,
|
||||
};
|
||||
|
||||
if !cx.tcx.sess.opts.unstable_opts.inline_llvm {
|
||||
// disable LLVM inlining
|
||||
return Some(AttributeKind::NoInline.create_attr(cx.llcx));
|
||||
|
|
@ -346,14 +356,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
OptimizeAttr::Speed => {}
|
||||
}
|
||||
|
||||
// `optnone` requires `noinline`
|
||||
let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
|
||||
(_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
|
||||
(InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint,
|
||||
(inline, _) => inline,
|
||||
};
|
||||
to_add.extend(inline_attr(cx, inline));
|
||||
|
||||
if cx.sess().must_emit_unwind_tables() {
|
||||
to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind));
|
||||
}
|
||||
|
|
@ -488,6 +490,14 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
let function_features =
|
||||
codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
|
||||
|
||||
// Apply function attributes as per usual if there are no user defined
|
||||
// target features otherwise this will get applied at the callsite.
|
||||
if function_features.is_empty() {
|
||||
if let Some(inline_attr) = inline_attr(cx, instance) {
|
||||
to_add.push(inline_attr);
|
||||
}
|
||||
}
|
||||
|
||||
let function_features = function_features
|
||||
.iter()
|
||||
// Convert to LLVMFeatures and filter out unavailable ones
|
||||
|
|
@ -517,6 +527,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
let function_features = function_features.iter().map(|s| s.as_str());
|
||||
let target_features: String =
|
||||
global_features.chain(function_features).intersperse(",").collect();
|
||||
|
||||
if !target_features.is_empty() {
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use object::{Object, ObjectSection};
|
|||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
|
||||
use rustc_codegen_ssa::{ModuleCodegen, looks_like_rust_object_file};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
|
|
@ -43,9 +43,7 @@ fn prepare_lto(
|
|||
.map(|symbol| CString::new(symbol.to_owned()).unwrap())
|
||||
.collect::<Vec<CString>>();
|
||||
|
||||
if cgcx.regular_module_config.instrument_coverage
|
||||
|| cgcx.regular_module_config.pgo_gen.enabled()
|
||||
{
|
||||
if cgcx.module_config.instrument_coverage || cgcx.module_config.pgo_gen.enabled() {
|
||||
// These are weak symbols that point to the profile version and the
|
||||
// profile name, which need to be treated as exported so LTO doesn't nix
|
||||
// them.
|
||||
|
|
@ -55,15 +53,15 @@ fn prepare_lto(
|
|||
symbols_below_threshold.extend(PROFILER_WEAK_SYMBOLS.iter().map(|&sym| sym.to_owned()));
|
||||
}
|
||||
|
||||
if cgcx.regular_module_config.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
if cgcx.module_config.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
let mut msan_weak_symbols = Vec::new();
|
||||
|
||||
// Similar to profiling, preserve weak msan symbol during LTO.
|
||||
if cgcx.regular_module_config.sanitizer_recover.contains(SanitizerSet::MEMORY) {
|
||||
if cgcx.module_config.sanitizer_recover.contains(SanitizerSet::MEMORY) {
|
||||
msan_weak_symbols.push(c"__msan_keep_going");
|
||||
}
|
||||
|
||||
if cgcx.regular_module_config.sanitizer_memory_track_origins != 0 {
|
||||
if cgcx.module_config.sanitizer_memory_track_origins != 0 {
|
||||
msan_weak_symbols.push(c"__msan_track_origins");
|
||||
}
|
||||
|
||||
|
|
@ -227,15 +225,9 @@ fn fat_lto(
|
|||
// All the other modules will be serialized and reparsed into the new
|
||||
// context, so this hopefully avoids serializing and parsing the largest
|
||||
// codegen unit.
|
||||
//
|
||||
// Additionally use a regular module as the base here to ensure that various
|
||||
// file copy operations in the backend work correctly. The only other kind
|
||||
// of module here should be an allocator one, and if your crate is smaller
|
||||
// than the allocator module then the size doesn't really matter anyway.
|
||||
let costliest_module = in_memory
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, module)| module.kind == ModuleKind::Regular)
|
||||
.map(|(i, module)| {
|
||||
let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) };
|
||||
(cost, i)
|
||||
|
|
@ -583,7 +575,7 @@ pub(crate) fn run_pass_manager(
|
|||
thin: bool,
|
||||
) {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
|
||||
let config = cgcx.config(module.kind);
|
||||
let config = &cgcx.module_config;
|
||||
|
||||
// Now we have one massive module inside of llmod. Time to run the
|
||||
// LTO-specific optimization passes that LLVM provides.
|
||||
|
|
@ -745,7 +737,7 @@ pub(crate) fn optimize_thin_module(
|
|||
let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx);
|
||||
let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm);
|
||||
// Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here.
|
||||
if cgcx.config(ModuleKind::Regular).embed_bitcode() {
|
||||
if cgcx.module_config.embed_bitcode() {
|
||||
module.thin_lto_buffer = Some(thin_module.data().to_vec());
|
||||
}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1392,7 +1392,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
fn call(
|
||||
&mut self,
|
||||
llty: &'ll Type,
|
||||
fn_attrs: Option<&CodegenFnAttrs>,
|
||||
fn_call_attrs: Option<&CodegenFnAttrs>,
|
||||
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
||||
llfn: &'ll Value,
|
||||
args: &[&'ll Value],
|
||||
|
|
@ -1409,10 +1409,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
}
|
||||
|
||||
// Emit CFI pointer type membership test
|
||||
self.cfi_type_test(fn_attrs, fn_abi, instance, llfn);
|
||||
self.cfi_type_test(fn_call_attrs, fn_abi, instance, llfn);
|
||||
|
||||
// Emit KCFI operand bundle
|
||||
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn);
|
||||
let kcfi_bundle = self.kcfi_operand_bundle(fn_call_attrs, fn_abi, instance, llfn);
|
||||
if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) {
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
|
|
@ -1429,6 +1429,29 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
c"".as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(instance) = instance {
|
||||
// Attributes on the function definition being called
|
||||
let fn_defn_attrs = self.cx.tcx.codegen_fn_attrs(instance.def_id());
|
||||
if let Some(fn_call_attrs) = fn_call_attrs
|
||||
&& !fn_call_attrs.target_features.is_empty()
|
||||
// If there is an inline attribute and a target feature that matches
|
||||
// we will add the attribute to the callsite otherwise we'll omit
|
||||
// this and not add the attribute to prevent soundness issues.
|
||||
&& let Some(inlining_rule) = attributes::inline_attr(&self.cx, instance)
|
||||
&& self.cx.tcx.is_target_feature_call_safe(
|
||||
&fn_call_attrs.target_features,
|
||||
&fn_defn_attrs.target_features,
|
||||
)
|
||||
{
|
||||
attributes::apply_to_callsite(
|
||||
call,
|
||||
llvm::AttributePlace::Function,
|
||||
&[inlining_rule],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(fn_abi) = fn_abi {
|
||||
fn_abi.apply_attrs_callsite(self, call);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,10 @@ pub(crate) unsafe fn create_module<'ll>(
|
|||
// LLVM 22.0 updated the default layout on avr: https://github.com/llvm/llvm-project/pull/153010
|
||||
target_data_layout = target_data_layout.replace("n8:16", "n8")
|
||||
}
|
||||
if sess.target.arch == "nvptx64" {
|
||||
// LLVM 22 updated the NVPTX layout to indicate 256-bit vector load/store: https://github.com/llvm/llvm-project/pull/155198
|
||||
target_data_layout = target_data_layout.replace("-i256:256", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the data-layout values hardcoded remain the defaults.
|
||||
|
|
|
|||
|
|
@ -383,7 +383,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
| sym::rotate_left
|
||||
| sym::rotate_right
|
||||
| sym::saturating_add
|
||||
| sym::saturating_sub => {
|
||||
| sym::saturating_sub
|
||||
| sym::unchecked_funnel_shl
|
||||
| sym::unchecked_funnel_shr => {
|
||||
let ty = args[0].layout.ty;
|
||||
if !ty.is_integral() {
|
||||
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
|
||||
|
|
@ -424,18 +426,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
sym::bitreverse => {
|
||||
self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
|
||||
}
|
||||
sym::rotate_left | sym::rotate_right => {
|
||||
let is_left = name == sym::rotate_left;
|
||||
let val = args[0].immediate();
|
||||
let raw_shift = args[1].immediate();
|
||||
// rotate = funnel shift with first two args the same
|
||||
sym::rotate_left
|
||||
| sym::rotate_right
|
||||
| sym::unchecked_funnel_shl
|
||||
| sym::unchecked_funnel_shr => {
|
||||
let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl;
|
||||
let lhs = args[0].immediate();
|
||||
let (rhs, raw_shift) =
|
||||
if name == sym::rotate_left || name == sym::rotate_right {
|
||||
// rotate = funnel shift with first two args the same
|
||||
(lhs, args[1].immediate())
|
||||
} else {
|
||||
(args[1].immediate(), args[2].immediate())
|
||||
};
|
||||
let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' });
|
||||
|
||||
// llvm expects shift to be the same type as the values, but rust
|
||||
// always uses `u32`.
|
||||
let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
|
||||
let raw_shift = self.intcast(raw_shift, self.val_ty(lhs), false);
|
||||
|
||||
self.call_intrinsic(llvm_name, &[llty], &[val, val, raw_shift])
|
||||
self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs, raw_shift])
|
||||
}
|
||||
sym::saturating_add | sym::saturating_sub => {
|
||||
let is_add = name == sym::saturating_add;
|
||||
|
|
|
|||
|
|
@ -40,15 +40,15 @@ codegen_ssa_dynamic_linking_with_lto =
|
|||
.note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
|
||||
|
||||
codegen_ssa_error_calling_dlltool =
|
||||
Error calling dlltool '{$dlltool_path}': {$error}
|
||||
error calling dlltool '{$dlltool_path}': {$error}
|
||||
|
||||
codegen_ssa_error_creating_import_library =
|
||||
Error creating import library for {$lib_name}: {$error}
|
||||
error creating import library for {$lib_name}: {$error}
|
||||
|
||||
codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
|
||||
|
||||
codegen_ssa_error_writing_def_file =
|
||||
Error writing .DEF file: {$error}
|
||||
error writing .DEF file: {$error}
|
||||
|
||||
codegen_ssa_expected_name_value_pair = expected name value pair
|
||||
|
||||
|
|
@ -264,9 +264,9 @@ codegen_ssa_shuffle_indices_evaluation = could not evaluate shuffle_indices at c
|
|||
|
||||
codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libraries to link
|
||||
|
||||
codegen_ssa_static_library_native_artifacts = Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.
|
||||
codegen_ssa_static_library_native_artifacts = link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.
|
||||
|
||||
codegen_ssa_static_library_native_artifacts_to_file = Native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms.
|
||||
codegen_ssa_static_library_native_artifacts_to_file = native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms.
|
||||
|
||||
codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` failed: {$status}
|
||||
.note = {$output}
|
||||
|
|
@ -364,13 +364,13 @@ codegen_ssa_unable_to_run = unable to run `{$util}`: {$error}
|
|||
|
||||
codegen_ssa_unable_to_run_dsymutil = unable to run `dsymutil`: {$error}
|
||||
|
||||
codegen_ssa_unable_to_write_debugger_visualizer = Unable to write debugger visualizer file `{$path}`: {$error}
|
||||
codegen_ssa_unable_to_write_debugger_visualizer = unable to write debugger visualizer file `{$path}`: {$error}
|
||||
|
||||
codegen_ssa_unexpected_parameter_name = unexpected parameter name
|
||||
.label = expected `{$prefix_nops}` or `{$entry_nops}`
|
||||
|
||||
codegen_ssa_unknown_archive_kind =
|
||||
Don't know how to build archive of type: {$kind}
|
||||
don't know how to build archive of type: {$kind}
|
||||
|
||||
codegen_ssa_unknown_ctarget_feature =
|
||||
unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ use super::linker::{self, Linker};
|
|||
use super::metadata::{MetadataPosition, create_wrapper_file};
|
||||
use super::rpath::{self, RPathConfig};
|
||||
use super::{apple, versioned_llvm_target};
|
||||
use crate::base::needs_allocator_shim_for_linking;
|
||||
use crate::{
|
||||
CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
|
||||
};
|
||||
|
|
@ -2080,9 +2081,17 @@ fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &Codeg
|
|||
}
|
||||
|
||||
/// Add object files for allocator code linked once for the whole crate tree.
|
||||
fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
|
||||
if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) {
|
||||
cmd.add_object(obj);
|
||||
fn add_local_crate_allocator_objects(
|
||||
cmd: &mut dyn Linker,
|
||||
codegen_results: &CodegenResults,
|
||||
crate_type: CrateType,
|
||||
) {
|
||||
if needs_allocator_shim_for_linking(&codegen_results.crate_info.dependency_formats, crate_type)
|
||||
{
|
||||
if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref())
|
||||
{
|
||||
cmd.add_object(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2281,7 +2290,7 @@ fn linker_with_args(
|
|||
codegen_results,
|
||||
metadata,
|
||||
);
|
||||
add_local_crate_allocator_objects(cmd, codegen_results);
|
||||
add_local_crate_allocator_objects(cmd, codegen_results, crate_type);
|
||||
|
||||
// Avoid linking to dynamic libraries unless they satisfy some undefined symbols
|
||||
// at the point at which they are specified on the command line.
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ use rustc_metadata::{
|
|||
};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
use rustc_middle::middle::exported_symbols;
|
||||
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
|
||||
use rustc_middle::middle::exported_symbols::{
|
||||
self, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
|
||||
|
|
@ -22,6 +23,8 @@ use tracing::{debug, warn};
|
|||
|
||||
use super::command::Command;
|
||||
use super::symbol_export;
|
||||
use crate::back::symbol_export::allocator_shim_symbols;
|
||||
use crate::base::needs_allocator_shim_for_linking;
|
||||
use crate::errors;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -1827,7 +1830,7 @@ fn exported_symbols_for_non_proc_macro(
|
|||
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
|
||||
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
|
||||
// Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins
|
||||
// from any cdylib. The latter doesn't work anyway as we use hidden visibility for
|
||||
// from any dylib. The latter doesn't work anyway as we use hidden visibility for
|
||||
// compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning.
|
||||
if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {
|
||||
symbols.push((
|
||||
|
|
@ -1838,6 +1841,14 @@ fn exported_symbols_for_non_proc_macro(
|
|||
}
|
||||
});
|
||||
|
||||
// Mark allocator shim symbols as exported only if they were generated.
|
||||
if export_threshold == SymbolExportLevel::Rust
|
||||
&& needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type)
|
||||
&& tcx.allocator_kind(()).is_some()
|
||||
{
|
||||
symbols.extend(allocator_shim_symbols(tcx));
|
||||
}
|
||||
|
||||
symbols
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::ffi::CString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel};
|
||||
|
|
@ -8,8 +9,9 @@ use rustc_middle::ty::TyCtxt;
|
|||
use rustc_session::config::{CrateType, Lto};
|
||||
use tracing::info;
|
||||
|
||||
use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate};
|
||||
use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate};
|
||||
use crate::back::write::CodegenContext;
|
||||
use crate::base::allocator_kind_for_codegen;
|
||||
use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro};
|
||||
use crate::traits::*;
|
||||
|
||||
|
|
@ -94,6 +96,19 @@ pub(super) fn exported_symbols_for_lto(
|
|||
.filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| {
|
||||
if info.level.is_below_threshold(export_threshold) || info.used {
|
||||
Some(symbol_name_for_instance_in_crate(tcx, s, cnum))
|
||||
} else if export_threshold == SymbolExportLevel::C
|
||||
&& info.rustc_std_internal_symbol
|
||||
&& let Some(AllocatorKind::Default) = allocator_kind_for_codegen(tcx)
|
||||
{
|
||||
// Export the __rdl_* exports for usage by the allocator shim when not using
|
||||
// #[global_allocator]. Most of the conditions above are only used to avoid
|
||||
// unnecessary expensive symbol_name_for_instance_in_crate calls.
|
||||
let sym = symbol_name_for_instance_in_crate(tcx, s, cnum);
|
||||
if sym.contains("__rdl_") || sym.contains("__rg_oom") {
|
||||
Some(sym)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -115,6 +130,11 @@ pub(super) fn exported_symbols_for_lto(
|
|||
}
|
||||
}
|
||||
|
||||
// Mark allocator shim symbols as exported only if they were generated.
|
||||
if export_threshold == SymbolExportLevel::Rust && allocator_kind_for_codegen(tcx).is_some() {
|
||||
symbols_below_threshold.extend(allocator_shim_symbols(tcx).map(|(name, _kind)| name));
|
||||
}
|
||||
|
||||
symbols_below_threshold
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use rustc_symbol_mangling::mangle_internal_symbol;
|
|||
use rustc_target::spec::TlsModel;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::base::allocator_kind_for_codegen;
|
||||
use crate::back::symbol_export;
|
||||
|
||||
fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
|
||||
crates_export_threshold(tcx.crate_types())
|
||||
|
|
@ -217,31 +217,6 @@ fn exported_non_generic_symbols_provider_local<'tcx>(
|
|||
));
|
||||
}
|
||||
|
||||
// Mark allocator shim symbols as exported only if they were generated.
|
||||
if allocator_kind_for_codegen(tcx).is_some() {
|
||||
for symbol_name in ALLOCATOR_METHODS
|
||||
.iter()
|
||||
.map(|method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()))
|
||||
.chain([
|
||||
mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
|
||||
mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
|
||||
mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
|
||||
])
|
||||
{
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
|
||||
|
||||
symbols.push((
|
||||
exported_symbol,
|
||||
SymbolExportInfo {
|
||||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
rustc_std_internal_symbol: true,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort so we get a stable incr. comp. hash.
|
||||
symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx));
|
||||
|
||||
|
|
@ -516,6 +491,31 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
upstream_monomorphizations_for_provider;
|
||||
}
|
||||
|
||||
pub(crate) fn allocator_shim_symbols(
|
||||
tcx: TyCtxt<'_>,
|
||||
) -> impl Iterator<Item = (String, SymbolExportKind)> {
|
||||
ALLOCATOR_METHODS
|
||||
.iter()
|
||||
.map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()))
|
||||
.chain([
|
||||
mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
|
||||
mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
|
||||
mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
|
||||
])
|
||||
.map(move |symbol_name| {
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
|
||||
|
||||
(
|
||||
symbol_export::exporting_symbol_name_for_instance_in_crate(
|
||||
tcx,
|
||||
exported_symbol,
|
||||
LOCAL_CRATE,
|
||||
),
|
||||
SymbolExportKind::Text,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel {
|
||||
// We export anything that's not mangled at the "C" layer as it probably has
|
||||
// to do with ABI concerns. We do not, however, apply such treatment to
|
||||
|
|
|
|||
|
|
@ -333,8 +333,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
|||
pub crate_types: Vec<CrateType>,
|
||||
pub output_filenames: Arc<OutputFilenames>,
|
||||
pub invocation_temp: Option<String>,
|
||||
pub regular_module_config: Arc<ModuleConfig>,
|
||||
pub allocator_module_config: Arc<ModuleConfig>,
|
||||
pub module_config: Arc<ModuleConfig>,
|
||||
pub tm_factory: TargetMachineFactoryFn<B>,
|
||||
pub msvc_imps_needed: bool,
|
||||
pub is_pe_coff: bool,
|
||||
|
|
@ -372,13 +371,6 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
|
|||
pub fn create_dcx(&self) -> DiagCtxt {
|
||||
DiagCtxt::new(Box::new(self.diag_emitter.clone()))
|
||||
}
|
||||
|
||||
pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
|
||||
match kind {
|
||||
ModuleKind::Regular => &self.regular_module_config,
|
||||
ModuleKind::Allocator => &self.allocator_module_config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_thin_lto_work<B: ExtraBackendMethods>(
|
||||
|
|
@ -442,6 +434,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
|
|||
backend: B,
|
||||
tcx: TyCtxt<'_>,
|
||||
target_cpu: String,
|
||||
allocator_module: Option<ModuleCodegen<B::Module>>,
|
||||
) -> OngoingCodegen<B> {
|
||||
let (coordinator_send, coordinator_receive) = channel();
|
||||
|
||||
|
|
@ -465,6 +458,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
|
|||
coordinator_receive,
|
||||
Arc::new(regular_config),
|
||||
Arc::new(allocator_config),
|
||||
allocator_module,
|
||||
coordinator_send.clone(),
|
||||
);
|
||||
|
||||
|
|
@ -495,7 +489,7 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
|
|||
|
||||
let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir");
|
||||
|
||||
for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
|
||||
for module in &compiled_modules.modules {
|
||||
let mut files = Vec::new();
|
||||
if let Some(object_file_path) = &module.object {
|
||||
files.push((OutputType::Object.extension(), object_file_path.as_path()));
|
||||
|
|
@ -720,15 +714,6 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> {
|
|||
}
|
||||
|
||||
impl<B: WriteBackendMethods> WorkItem<B> {
|
||||
fn module_kind(&self) -> ModuleKind {
|
||||
match *self {
|
||||
WorkItem::Optimize(ref m) => m.kind,
|
||||
WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto { .. } | WorkItem::ThinLto(_) => {
|
||||
ModuleKind::Regular
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a short description of this work item suitable for use as a thread name.
|
||||
fn short_description(&self) -> String {
|
||||
// `pthread_setname()` on *nix ignores anything beyond the first 15
|
||||
|
|
@ -809,19 +794,12 @@ pub(crate) fn compute_per_cgu_lto_type(
|
|||
sess_lto: &Lto,
|
||||
opts: &config::Options,
|
||||
sess_crate_types: &[CrateType],
|
||||
module_kind: ModuleKind,
|
||||
) -> ComputedLtoType {
|
||||
// If the linker does LTO, we don't have to do it. Note that we
|
||||
// keep doing full LTO, if it is requested, as not to break the
|
||||
// assumption that the output will be a single module.
|
||||
let linker_does_lto = opts.cg.linker_plugin_lto.enabled();
|
||||
|
||||
// When we're automatically doing ThinLTO for multi-codegen-unit
|
||||
// builds we don't actually want to LTO the allocator modules if
|
||||
// it shows up. This is due to various linker shenanigans that
|
||||
// we'll encounter later.
|
||||
let is_allocator = module_kind == ModuleKind::Allocator;
|
||||
|
||||
// We ignore a request for full crate graph LTO if the crate type
|
||||
// is only an rlib, as there is no full crate graph to process,
|
||||
// that'll happen later.
|
||||
|
|
@ -833,7 +811,7 @@ pub(crate) fn compute_per_cgu_lto_type(
|
|||
let is_rlib = matches!(sess_crate_types, [CrateType::Rlib]);
|
||||
|
||||
match sess_lto {
|
||||
Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin,
|
||||
Lto::ThinLocal if !linker_does_lto => ComputedLtoType::Thin,
|
||||
Lto::Thin if !linker_does_lto && !is_rlib => ComputedLtoType::Thin,
|
||||
Lto::Fat if !is_rlib => ComputedLtoType::Fat,
|
||||
_ => ComputedLtoType::No,
|
||||
|
|
@ -843,23 +821,22 @@ pub(crate) fn compute_per_cgu_lto_type(
|
|||
fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
mut module: ModuleCodegen<B::Module>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> WorkItemResult<B> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
|
||||
B::optimize(cgcx, dcx, &mut module, module_config);
|
||||
B::optimize(cgcx, dcx, &mut module, &cgcx.module_config);
|
||||
|
||||
// After we've done the initial round of optimizations we need to
|
||||
// decide whether to synchronously codegen this module or ship it
|
||||
// back to the coordinator thread for further LTO processing (which
|
||||
// has to wait for all the initial modules to be optimized).
|
||||
|
||||
let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types, module.kind);
|
||||
let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types);
|
||||
|
||||
// If we're doing some form of incremental LTO then we need to be sure to
|
||||
// save our module to disk first.
|
||||
let bitcode = if cgcx.config(module.kind).emit_pre_lto_bc {
|
||||
let bitcode = if cgcx.module_config.emit_pre_lto_bc {
|
||||
let filename = pre_lto_bitcode_filename(&module.name);
|
||||
cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename))
|
||||
} else {
|
||||
|
|
@ -868,7 +845,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||
|
||||
match lto_type {
|
||||
ComputedLtoType::No => {
|
||||
let module = B::codegen(cgcx, module, module_config);
|
||||
let module = B::codegen(cgcx, module, &cgcx.module_config);
|
||||
WorkItemResult::Finished(module)
|
||||
}
|
||||
ComputedLtoType::Thin => {
|
||||
|
|
@ -899,7 +876,6 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||
fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
module: CachedModuleCodegen,
|
||||
module_config: &ModuleConfig,
|
||||
) -> WorkItemResult<B> {
|
||||
let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap();
|
||||
|
||||
|
|
@ -959,6 +935,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
|||
}
|
||||
};
|
||||
|
||||
let module_config = &cgcx.module_config;
|
||||
let should_emit_obj = module_config.emit_obj != EmitObj::None;
|
||||
let assembly = load_from_incr_cache(module_config.emit_asm, OutputType::Assembly);
|
||||
let llvm_ir = load_from_incr_cache(module_config.emit_ir, OutputType::LlvmAssembly);
|
||||
|
|
@ -971,7 +948,6 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
|||
WorkItemResult::Finished(CompiledModule {
|
||||
links_from_incr_cache,
|
||||
name: module.name,
|
||||
kind: ModuleKind::Regular,
|
||||
object,
|
||||
dwarf_object,
|
||||
bytecode,
|
||||
|
|
@ -986,7 +962,6 @@ fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
|
|||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
mut needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> WorkItemResult<B> {
|
||||
for (module, wp) in import_only_modules {
|
||||
needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module })
|
||||
|
|
@ -998,17 +973,16 @@ fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
|
|||
each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
);
|
||||
let module = B::codegen(cgcx, module, module_config);
|
||||
let module = B::codegen(cgcx, module, &cgcx.module_config);
|
||||
WorkItemResult::Finished(module)
|
||||
}
|
||||
|
||||
fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
module: lto::ThinModule<B>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> WorkItemResult<B> {
|
||||
let module = B::optimize_thin(cgcx, module);
|
||||
let module = B::codegen(cgcx, module, module_config);
|
||||
let module = B::codegen(cgcx, module, &cgcx.module_config);
|
||||
WorkItemResult::Finished(module)
|
||||
}
|
||||
|
||||
|
|
@ -1093,6 +1067,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
coordinator_receive: Receiver<Message<B>>,
|
||||
regular_config: Arc<ModuleConfig>,
|
||||
allocator_config: Arc<ModuleConfig>,
|
||||
allocator_module: Option<ModuleCodegen<B::Module>>,
|
||||
tx_to_llvm_workers: Sender<Message<B>>,
|
||||
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
|
||||
let coordinator_send = tx_to_llvm_workers;
|
||||
|
|
@ -1157,8 +1132,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
expanded_args: tcx.sess.expanded_args.clone(),
|
||||
diag_emitter: shared_emitter.clone(),
|
||||
output_filenames: Arc::clone(tcx.output_filenames(())),
|
||||
regular_module_config: regular_config,
|
||||
allocator_module_config: allocator_config,
|
||||
module_config: regular_config,
|
||||
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
|
||||
msvc_imps_needed: msvc_imps_needed(tcx),
|
||||
is_pe_coff: tcx.sess.target.is_like_windows,
|
||||
|
|
@ -1173,6 +1147,11 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
invocation_temp: sess.invocation_temp.clone(),
|
||||
};
|
||||
|
||||
let compiled_allocator_module = allocator_module.map(|mut allocator_module| {
|
||||
B::optimize(&cgcx, tcx.sess.dcx(), &mut allocator_module, &allocator_config);
|
||||
B::codegen(&cgcx, allocator_module, &allocator_config)
|
||||
});
|
||||
|
||||
// This is the "main loop" of parallel work happening for parallel codegen.
|
||||
// It's here that we manage parallelism, schedule work, and work with
|
||||
// messages coming from clients.
|
||||
|
|
@ -1312,7 +1291,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
// This is where we collect codegen units that have gone all the way
|
||||
// through codegen and LLVM.
|
||||
let mut compiled_modules = vec![];
|
||||
let mut compiled_allocator_module = None;
|
||||
let mut needs_fat_lto = Vec::new();
|
||||
let mut needs_thin_lto = Vec::new();
|
||||
let mut lto_import_only_modules = Vec::new();
|
||||
|
|
@ -1586,15 +1564,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
|
||||
match result {
|
||||
Ok(WorkItemResult::Finished(compiled_module)) => {
|
||||
match compiled_module.kind {
|
||||
ModuleKind::Regular => {
|
||||
compiled_modules.push(compiled_module);
|
||||
}
|
||||
ModuleKind::Allocator => {
|
||||
assert!(compiled_allocator_module.is_none());
|
||||
compiled_allocator_module = Some(compiled_module);
|
||||
}
|
||||
}
|
||||
compiled_modules.push(compiled_module);
|
||||
}
|
||||
Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => {
|
||||
assert!(!started_lto);
|
||||
|
|
@ -1722,45 +1692,38 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
let cgcx = cgcx.clone();
|
||||
|
||||
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
|
||||
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let module_config = cgcx.config(work.module_kind());
|
||||
|
||||
match work {
|
||||
WorkItem::Optimize(m) => {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name);
|
||||
execute_optimize_work_item(&cgcx, m, module_config)
|
||||
}
|
||||
WorkItem::CopyPostLtoArtifacts(m) => {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg(
|
||||
"codegen_copy_artifacts_from_incr_cache",
|
||||
&*m.name,
|
||||
);
|
||||
execute_copy_from_cache_work_item(&cgcx, m, module_config)
|
||||
}
|
||||
WorkItem::FatLto {
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
|
||||
WorkItem::Optimize(m) => {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name);
|
||||
execute_optimize_work_item(&cgcx, m)
|
||||
}
|
||||
WorkItem::CopyPostLtoArtifacts(m) => {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*m.name);
|
||||
execute_copy_from_cache_work_item(&cgcx, m)
|
||||
}
|
||||
WorkItem::FatLto {
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
} => {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", "everything");
|
||||
execute_fat_lto_work_item(
|
||||
&cgcx,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
} => {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("codegen_module_perform_lto", "everything");
|
||||
execute_fat_lto_work_item(
|
||||
&cgcx,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
module_config,
|
||||
)
|
||||
}
|
||||
WorkItem::ThinLto(m) => {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name());
|
||||
execute_thin_lto_work_item(&cgcx, m, module_config)
|
||||
}
|
||||
)
|
||||
}
|
||||
WorkItem::ThinLto(m) => {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name());
|
||||
execute_thin_lto_work_item(&cgcx, m)
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use rustc_hir::lang_items::LangItem;
|
|||
use rustc_hir::{ItemId, Target};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
||||
use rustc_middle::middle::dependency_format::Dependencies;
|
||||
use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
|
||||
use rustc_middle::middle::lang_items;
|
||||
use rustc_middle::mir::BinOp;
|
||||
|
|
@ -45,9 +46,7 @@ use crate::meth::load_vtable;
|
|||
use crate::mir::operand::OperandValue;
|
||||
use crate::mir::place::PlaceRef;
|
||||
use crate::traits::*;
|
||||
use crate::{
|
||||
CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir,
|
||||
};
|
||||
use crate::{CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, errors, meth, mir};
|
||||
|
||||
pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
|
||||
match (op, signed) {
|
||||
|
|
@ -630,14 +629,30 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> {
|
|||
// If the crate doesn't have an `allocator_kind` set then there's definitely
|
||||
// no shim to generate. Otherwise we also check our dependency graph for all
|
||||
// our output crate types. If anything there looks like its a `Dynamic`
|
||||
// linkage, then it's already got an allocator shim and we'll be using that
|
||||
// one instead. If nothing exists then it's our job to generate the
|
||||
// allocator!
|
||||
let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| {
|
||||
// linkage for all crate types we may link as, then it's already got an
|
||||
// allocator shim and we'll be using that one instead. If nothing exists
|
||||
// then it's our job to generate the allocator! If crate types disagree
|
||||
// about whether an allocator shim is necessary or not, we generate one
|
||||
// and let needs_allocator_shim_for_linking decide at link time whether or
|
||||
// not to use it for any particular linker invocation.
|
||||
let all_crate_types_any_dynamic_crate = tcx.dependency_formats(()).iter().all(|(_, list)| {
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
list.iter().any(|&linkage| linkage == Linkage::Dynamic)
|
||||
});
|
||||
if any_dynamic_crate { None } else { tcx.allocator_kind(()) }
|
||||
if all_crate_types_any_dynamic_crate { None } else { tcx.allocator_kind(()) }
|
||||
}
|
||||
|
||||
/// Decide if this particular crate type needs an allocator shim linked in.
|
||||
/// This may return true even when allocator_kind_for_codegen returns false. In
|
||||
/// this case no allocator shim shall be linked.
|
||||
pub(crate) fn needs_allocator_shim_for_linking(
|
||||
dependency_formats: &Dependencies,
|
||||
crate_type: CrateType,
|
||||
) -> bool {
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
let any_dynamic_crate =
|
||||
dependency_formats[&crate_type].iter().any(|&linkage| linkage == Linkage::Dynamic);
|
||||
!any_dynamic_crate
|
||||
}
|
||||
|
||||
pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||
|
|
@ -647,7 +662,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
) -> OngoingCodegen<B> {
|
||||
// Skip crate items and just output metadata in -Z no-codegen mode.
|
||||
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
|
||||
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu);
|
||||
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, None);
|
||||
|
||||
ongoing_codegen.codegen_finished(tcx);
|
||||
|
||||
|
|
@ -678,7 +693,27 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
}
|
||||
}
|
||||
|
||||
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu);
|
||||
// Codegen an allocator shim, if necessary.
|
||||
let allocator_module = if let Some(kind) = allocator_kind_for_codegen(tcx) {
|
||||
let llmod_id =
|
||||
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
|
||||
|
||||
tcx.sess.time("write_allocator_module", || {
|
||||
let module = backend.codegen_allocator(
|
||||
tcx,
|
||||
&llmod_id,
|
||||
kind,
|
||||
// If allocator_kind is Some then alloc_error_handler_kind must
|
||||
// also be Some.
|
||||
tcx.alloc_error_handler_kind(()).unwrap(),
|
||||
);
|
||||
Some(ModuleCodegen::new_allocator(llmod_id, module))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, 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
|
||||
|
|
@ -795,35 +830,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
}
|
||||
}
|
||||
|
||||
// Codegen an allocator shim, if necessary.
|
||||
// Do this last to ensure the LLVM_passes timer doesn't start while no CGUs have been codegened
|
||||
// yet for the backend to optimize.
|
||||
if let Some(kind) = allocator_kind_for_codegen(tcx) {
|
||||
let llmod_id =
|
||||
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
|
||||
let module_llvm = tcx.sess.time("write_allocator_module", || {
|
||||
backend.codegen_allocator(
|
||||
tcx,
|
||||
&llmod_id,
|
||||
kind,
|
||||
// If allocator_kind is Some then alloc_error_handler_kind must
|
||||
// also be Some.
|
||||
tcx.alloc_error_handler_kind(()).unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
ongoing_codegen.wait_for_signal_to_codegen_item();
|
||||
ongoing_codegen.check_for_errors(tcx.sess);
|
||||
|
||||
// These modules are generally cheap and won't throw off scheduling.
|
||||
let cost = 0;
|
||||
submit_codegened_module_to_llvm(
|
||||
&ongoing_codegen.coordinator,
|
||||
ModuleCodegen::new_allocator(llmod_id, module_llvm),
|
||||
cost,
|
||||
);
|
||||
}
|
||||
|
||||
ongoing_codegen.codegen_finished(tcx);
|
||||
|
||||
// Since the main thread is sometimes blocked during codegen, we keep track
|
||||
|
|
@ -1118,12 +1124,7 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) ->
|
|||
// We can re-use either the pre- or the post-thinlto state. If no LTO is
|
||||
// being performed then we can use post-LTO artifacts, otherwise we must
|
||||
// reuse pre-LTO artifacts
|
||||
match compute_per_cgu_lto_type(
|
||||
&tcx.sess.lto(),
|
||||
&tcx.sess.opts,
|
||||
tcx.crate_types(),
|
||||
ModuleKind::Regular,
|
||||
) {
|
||||
match compute_per_cgu_lto_type(&tcx.sess.lto(), &tcx.sess.opts, tcx.crate_types()) {
|
||||
ComputedLtoType::No => CguReuse::PostLto,
|
||||
_ => CguReuse::PreLto,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -428,9 +428,16 @@ fn check_result(
|
|||
// llvm/llvm-project#70563).
|
||||
if !codegen_fn_attrs.target_features.is_empty()
|
||||
&& matches!(codegen_fn_attrs.inline, InlineAttr::Always)
|
||||
&& !tcx.features().target_feature_inline_always()
|
||||
&& let Some(span) = interesting_spans.inline
|
||||
{
|
||||
tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
|
||||
feature_err(
|
||||
tcx.sess,
|
||||
sym::target_feature_inline_always,
|
||||
span,
|
||||
"cannot use `#[inline(always)]` with `#[target_feature]`",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
// warn that inline has no effect when no_sanitize is present
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ impl<M> ModuleCodegen<M> {
|
|||
|
||||
CompiledModule {
|
||||
name: self.name.clone(),
|
||||
kind: self.kind,
|
||||
object,
|
||||
dwarf_object,
|
||||
bytecode,
|
||||
|
|
@ -134,7 +133,6 @@ impl<M> ModuleCodegen<M> {
|
|||
#[derive(Debug, Encodable, Decodable)]
|
||||
pub struct CompiledModule {
|
||||
pub name: String,
|
||||
pub kind: ModuleKind,
|
||||
pub object: Option<PathBuf>,
|
||||
pub dwarf_object: Option<PathBuf>,
|
||||
pub bytecode: Option<PathBuf>,
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@ const_eval_validation_failure =
|
|||
it is undefined behavior to use this value
|
||||
|
||||
const_eval_validation_failure_note =
|
||||
The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
|
||||
const_eval_validation_front_matter_invalid_value = constructing invalid value
|
||||
const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ driver_impl_ice_version = rustc {$version} running on {$triple}
|
|||
|
||||
driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}`
|
||||
|
||||
driver_impl_rlink_empty_version_number = The input does not contain version number
|
||||
driver_impl_rlink_empty_version_number = the input does not contain version number
|
||||
|
||||
driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
|
||||
|
||||
|
|
@ -24,6 +24,6 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver
|
|||
|
||||
driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
|
||||
|
||||
driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
|
||||
driver_impl_rlink_wrong_file_type = the input does not look like a .rlink file
|
||||
|
||||
driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error}
|
||||
|
|
|
|||
|
|
@ -1160,7 +1160,7 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
// - It's only produce with JSON output.
|
||||
// - It's not emitted the usual way, via `emit_diagnostic`.
|
||||
// - The `$message_type` field is "unused_externs" rather than the usual
|
||||
// "diagnosic".
|
||||
// "diagnostic".
|
||||
//
|
||||
// We count it as a lint error because it has a lint level. The value
|
||||
// of `loud` (which comes from "unused-externs" or
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ use std::rc::Rc;
|
|||
|
||||
pub(crate) use NamedMatch::*;
|
||||
pub(crate) use ParseResult::*;
|
||||
use rustc_ast::token::{self, DocComment, NonterminalKind, Token};
|
||||
use rustc_ast::token::{self, DocComment, NonterminalKind, Token, TokenKind};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_lint_defs::pluralize;
|
||||
|
|
@ -397,7 +397,23 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool {
|
|||
{
|
||||
ident1.name == ident2.name && is_raw1 == is_raw2
|
||||
} else {
|
||||
t1.kind == t2.kind
|
||||
// Note: we SHOULD NOT use `t1.kind == t2.kind` here, and we should instead compare the
|
||||
// tokens using the special comparison logic below.
|
||||
// It makes sure that variants containing `InvisibleOrigin` will
|
||||
// never compare equal to one another.
|
||||
//
|
||||
// When we had AST-based nonterminals we couldn't compare them, and the
|
||||
// old `Nonterminal` type had an `eq` that always returned false,
|
||||
// resulting in this restriction:
|
||||
// <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment>
|
||||
// This comparison logic emulates that behaviour. We could consider lifting this
|
||||
// restriction now but there are still cases involving invisible
|
||||
// delimiters that make it harder than it first appears.
|
||||
match (t1.kind, t2.kind) {
|
||||
(TokenKind::OpenInvisible(_) | TokenKind::CloseInvisible(_), _)
|
||||
| (_, TokenKind::OpenInvisible(_) | TokenKind::CloseInvisible(_)) => false,
|
||||
(a, b) => a == b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -642,6 +642,8 @@ declare_features! (
|
|||
(unstable, super_let, "1.88.0", Some(139076)),
|
||||
/// Allows subtrait items to shadow supertrait items.
|
||||
(unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
|
||||
/// Allows the use of target_feature when a function is marked inline(always).
|
||||
(unstable, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574)),
|
||||
/// Allows using `#[thread_local]` on `static` items.
|
||||
(unstable, thread_local, "1.0.0", Some(29594)),
|
||||
/// Allows defining `trait X = A + B;` alias items.
|
||||
|
|
|
|||
|
|
@ -449,6 +449,9 @@ pub(crate) fn check_intrinsic_type(
|
|||
}
|
||||
sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)),
|
||||
sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), tcx.types.u32], param(0)),
|
||||
sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {
|
||||
(1, 0, vec![param(0), param(0), tcx.types.u32], param(0))
|
||||
}
|
||||
sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => {
|
||||
(1, 0, vec![param(0), param(0)], param(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -490,12 +490,8 @@ fn resolve_local<'tcx>(
|
|||
//
|
||||
// Iterate up to the enclosing destruction scope to find the same scope that will also
|
||||
// be used for the result of the block itself.
|
||||
while let Some(s) = visitor.cx.var_parent {
|
||||
let parent = visitor.scope_tree.parent_map.get(&s).cloned();
|
||||
if let Some(Scope { data: ScopeData::Destruction, .. }) = parent {
|
||||
break;
|
||||
}
|
||||
visitor.cx.var_parent = parent;
|
||||
if let Some(inner_scope) = visitor.cx.var_parent {
|
||||
(visitor.cx.var_parent, _) = visitor.scope_tree.default_temporary_scope(inner_scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_lint::LintStore;
|
|||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::CurrentGcx;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_parse::new_parser_from_source_str;
|
||||
use rustc_parse::new_parser_from_simple_source_str;
|
||||
use rustc_parse::parser::attr::AllowLeadingUnsafe;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_query_system::query::print_query_stack;
|
||||
|
|
@ -68,7 +68,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
|
|||
};
|
||||
}
|
||||
|
||||
match new_parser_from_source_str(&psess, filename, s.to_string()) {
|
||||
match new_parser_from_simple_source_str(&psess, filename, s.to_string()) {
|
||||
Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) {
|
||||
Ok(meta_item) if parser.token == token::Eof => {
|
||||
if meta_item.path.segments.len() != 1 {
|
||||
|
|
@ -166,7 +166,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
|
|||
error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
|
||||
};
|
||||
|
||||
let mut parser = match new_parser_from_source_str(&psess, filename, s.to_string()) {
|
||||
let mut parser = match new_parser_from_simple_source_str(&psess, filename, s.to_string()) {
|
||||
Ok(parser) => parser,
|
||||
Err(errs) => {
|
||||
errs.into_iter().for_each(|err| err.cancel());
|
||||
|
|
|
|||
|
|
@ -5141,3 +5141,57 @@ declare_lint! {
|
|||
"detects tail calls of functions marked with `#[track_caller]`",
|
||||
@feature_gate = explicit_tail_calls;
|
||||
}
|
||||
declare_lint! {
|
||||
/// The `inline_always_mismatching_target_features` lint will trigger when a
|
||||
/// function with the `#[inline(always)]` and `#[target_feature(enable = "...")]`
|
||||
/// attributes is called and cannot be inlined due to missing target features in the caller.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,ignore (fails on x86_64)
|
||||
/// #[inline(always)]
|
||||
/// #[target_feature(enable = "fp16")]
|
||||
/// unsafe fn callee() {
|
||||
/// // operations using fp16 types
|
||||
/// }
|
||||
///
|
||||
/// // Caller does not enable the required target feature
|
||||
/// fn caller() {
|
||||
/// unsafe { callee(); }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// caller();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// warning: call to `#[inline(always)]`-annotated `callee` requires the same target features. Function will not have `alwaysinline` attribute applied
|
||||
/// --> $DIR/builtin.rs:5192:14
|
||||
/// |
|
||||
/// 10 | unsafe { callee(); }
|
||||
/// | ^^^^^^^^
|
||||
/// |
|
||||
/// note: `fp16` target feature enabled in `callee` here but missing from `caller`
|
||||
/// --> $DIR/builtin.rs:5185:1
|
||||
/// |
|
||||
/// 3 | #[target_feature(enable = "fp16")]
|
||||
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
/// 4 | unsafe fn callee() {
|
||||
/// | ------------------
|
||||
/// = note: `#[warn(inline_always_mismatching_target_features)]` on by default
|
||||
/// warning: 1 warning emitted
|
||||
/// ```
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Inlining a function with a target feature attribute into a caller that
|
||||
/// lacks the corresponding target feature can lead to unsound behavior.
|
||||
/// LLVM may select the wrong instructions or registers, or reorder
|
||||
/// operations, potentially resulting in runtime errors.
|
||||
pub INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES,
|
||||
Warn,
|
||||
r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ impl CStore {
|
|||
match (&left_name_val, &right_name_val) {
|
||||
(Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {
|
||||
cmp::Ordering::Equal => {
|
||||
if l.0.tech_value != r.0.tech_value {
|
||||
if !l.1.consistent(&tcx.sess.opts, Some(&r.1)) {
|
||||
report_diff(
|
||||
&l.0.prefix,
|
||||
&l.0.name,
|
||||
|
|
@ -424,20 +424,28 @@ impl CStore {
|
|||
right_name_val = None;
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
|
||||
if !r.1.consistent(&tcx.sess.opts, None) {
|
||||
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
|
||||
}
|
||||
right_name_val = None;
|
||||
}
|
||||
cmp::Ordering::Less => {
|
||||
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
|
||||
if !l.1.consistent(&tcx.sess.opts, None) {
|
||||
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
|
||||
}
|
||||
left_name_val = None;
|
||||
}
|
||||
},
|
||||
(Some(l), None) => {
|
||||
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
|
||||
if !l.1.consistent(&tcx.sess.opts, None) {
|
||||
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
|
||||
}
|
||||
left_name_val = None;
|
||||
}
|
||||
(None, Some(r)) => {
|
||||
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
|
||||
if !r.1.consistent(&tcx.sess.opts, None) {
|
||||
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
|
||||
}
|
||||
right_name_val = None;
|
||||
}
|
||||
(None, None) => break,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ pub enum TargetFeatureKind {
|
|||
Forced,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, HashStable)]
|
||||
pub struct TargetFeature {
|
||||
/// The name of the target feature (e.g. "avx")
|
||||
pub name: Symbol,
|
||||
|
|
|
|||
|
|
@ -299,4 +299,43 @@ impl ScopeTree {
|
|||
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns the scope of non-lifetime-extended temporaries within a given scope, as well as
|
||||
/// whether we've recorded a potential backwards-incompatible change to lint on.
|
||||
/// Returns `None` when no enclosing temporary scope is found, such as for static items.
|
||||
pub fn default_temporary_scope(&self, inner: Scope) -> (Option<Scope>, Option<Scope>) {
|
||||
let mut id = inner;
|
||||
let mut backwards_incompatible = None;
|
||||
|
||||
while let Some(&p) = self.parent_map.get(&id) {
|
||||
match p.data {
|
||||
ScopeData::Destruction => {
|
||||
debug!("temporary_scope({inner:?}) = {id:?} [enclosing]");
|
||||
return (Some(id), backwards_incompatible);
|
||||
}
|
||||
ScopeData::IfThenRescope | ScopeData::MatchGuard => {
|
||||
debug!("temporary_scope({inner:?}) = {p:?} [enclosing]");
|
||||
return (Some(p), backwards_incompatible);
|
||||
}
|
||||
ScopeData::Node
|
||||
| ScopeData::CallSite
|
||||
| ScopeData::Arguments
|
||||
| ScopeData::IfThen
|
||||
| ScopeData::Remainder(_) => {
|
||||
// If we haven't already passed through a backwards-incompatible node,
|
||||
// then check if we are passing through one now and record it if so.
|
||||
// This is for now only working for cases where a temporary lifetime is
|
||||
// *shortened*.
|
||||
if backwards_incompatible.is_none() {
|
||||
backwards_incompatible =
|
||||
self.backwards_incompatible_scope.get(&p.local_id).copied();
|
||||
}
|
||||
id = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("temporary_scope({inner:?}) = None");
|
||||
(None, backwards_incompatible)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -738,8 +738,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
type ProbeRef = &'tcx inspect::Probe<TyCtxt<'tcx>>;
|
||||
fn mk_probe_ref(self, probe: inspect::Probe<Self>) -> &'tcx inspect::Probe<TyCtxt<'tcx>> {
|
||||
type Probe = &'tcx inspect::Probe<TyCtxt<'tcx>>;
|
||||
fn mk_probe(self, probe: inspect::Probe<Self>) -> &'tcx inspect::Probe<TyCtxt<'tcx>> {
|
||||
self.arena.alloc(probe)
|
||||
}
|
||||
fn evaluate_root_goal_for_proof_tree_raw(
|
||||
|
|
|
|||
|
|
@ -35,41 +35,8 @@ impl RvalueScopes {
|
|||
// if there's one. Static items, for instance, won't
|
||||
// have an enclosing scope, hence no scope will be
|
||||
// returned.
|
||||
let mut id = Scope { local_id: expr_id, data: ScopeData::Node };
|
||||
let mut backwards_incompatible = None;
|
||||
|
||||
while let Some(&p) = region_scope_tree.parent_map.get(&id) {
|
||||
match p.data {
|
||||
ScopeData::Destruction => {
|
||||
debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
|
||||
return (Some(id), backwards_incompatible);
|
||||
}
|
||||
ScopeData::IfThenRescope | ScopeData::MatchGuard => {
|
||||
debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
|
||||
return (Some(p), backwards_incompatible);
|
||||
}
|
||||
ScopeData::Node
|
||||
| ScopeData::CallSite
|
||||
| ScopeData::Arguments
|
||||
| ScopeData::IfThen
|
||||
| ScopeData::Remainder(_) => {
|
||||
// If we haven't already passed through a backwards-incompatible node,
|
||||
// then check if we are passing through one now and record it if so.
|
||||
// This is for now only working for cases where a temporary lifetime is
|
||||
// *shortened*.
|
||||
if backwards_incompatible.is_none() {
|
||||
backwards_incompatible = region_scope_tree
|
||||
.backwards_incompatible_scope
|
||||
.get(&p.local_id)
|
||||
.copied();
|
||||
}
|
||||
id = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("temporary_scope({expr_id:?}) = None");
|
||||
(None, backwards_incompatible)
|
||||
region_scope_tree
|
||||
.default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node })
|
||||
}
|
||||
|
||||
/// Make an association between a sub-expression and an extended lifetime
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
use rustc_hir::attrs::InlineAttr;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
|
||||
use rustc_middle::mir::{Body, TerminatorKind};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
||||
use crate::pass_manager::MirLint;
|
||||
|
||||
pub(super) struct CheckInlineAlwaysTargetFeature;
|
||||
|
||||
impl<'tcx> MirLint<'tcx> for CheckInlineAlwaysTargetFeature {
|
||||
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
||||
check_inline_always_target_features(tcx, body)
|
||||
}
|
||||
}
|
||||
|
||||
/// `#[target_feature]`-annotated functions can be marked `#[inline]` and will only be inlined if
|
||||
/// the target features match (as well as all of the other inlining heuristics). `#[inline(always)]`
|
||||
/// will always inline regardless of matching target features, which can result in errors from LLVM.
|
||||
/// However, it is desirable to be able to always annotate certain functions (e.g. SIMD intrinsics)
|
||||
/// as `#[inline(always)]` but check the target features match in Rust to avoid the LLVM errors.
|
||||
///
|
||||
/// We check the caller and callee target features to ensure that this can
|
||||
/// be done or emit a lint.
|
||||
#[inline]
|
||||
fn check_inline_always_target_features<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
||||
let caller_def_id = body.source.def_id().expect_local();
|
||||
if !tcx.def_kind(caller_def_id).has_codegen_attrs() {
|
||||
return;
|
||||
}
|
||||
|
||||
let caller_codegen_fn_attrs = tcx.codegen_fn_attrs(caller_def_id);
|
||||
|
||||
for bb in body.basic_blocks.iter() {
|
||||
let terminator = bb.terminator();
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => {
|
||||
let fn_ty = func.ty(body, tcx);
|
||||
let ty::FnDef(callee_def_id, _) = *fn_ty.kind() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !tcx.def_kind(callee_def_id).has_codegen_attrs() {
|
||||
continue;
|
||||
}
|
||||
let callee_codegen_fn_attrs = tcx.codegen_fn_attrs(callee_def_id);
|
||||
if callee_codegen_fn_attrs.inline != InlineAttr::Always
|
||||
|| callee_codegen_fn_attrs.target_features.is_empty()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scan the users defined target features and ensure they
|
||||
// match the caller.
|
||||
if tcx.is_target_feature_call_safe(
|
||||
&callee_codegen_fn_attrs.target_features,
|
||||
&caller_codegen_fn_attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(tcx.sess.target_features.iter().map(|feat| TargetFeature {
|
||||
name: *feat,
|
||||
kind: TargetFeatureKind::Implied,
|
||||
}))
|
||||
.collect::<Vec<_>>(),
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let callee_only: Vec<_> = callee_codegen_fn_attrs
|
||||
.target_features
|
||||
.iter()
|
||||
.filter(|it| !caller_codegen_fn_attrs.target_features.contains(it))
|
||||
.filter(|it| !matches!(it.kind, TargetFeatureKind::Implied))
|
||||
.map(|it| it.name.as_str())
|
||||
.collect();
|
||||
|
||||
crate::errors::emit_inline_always_target_feature_diagnostic(
|
||||
tcx,
|
||||
terminator.source_info.span,
|
||||
callee_def_id,
|
||||
caller_def_id.into(),
|
||||
&callee_only,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ use rustc_errors::codes::*;
|
|||
use rustc_errors::{Diag, LintDiagnostic};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint::{self, Lint};
|
||||
use rustc_span::def_id::DefId;
|
||||
|
|
@ -9,6 +10,46 @@ use rustc_span::{Ident, Span, Symbol};
|
|||
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
/// Emit diagnostic for calls to `#[inline(always)]`-annotated functions with a
|
||||
/// `#[target_feature]` attribute where the caller enables a different set of target features.
|
||||
pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
call_span: Span,
|
||||
callee_def_id: DefId,
|
||||
caller_def_id: DefId,
|
||||
callee_only: &[&'a str],
|
||||
) {
|
||||
let callee = tcx.def_path_str(callee_def_id);
|
||||
let caller = tcx.def_path_str(caller_def_id);
|
||||
|
||||
tcx.node_span_lint(
|
||||
lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES,
|
||||
tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()),
|
||||
call_span,
|
||||
|lint| {
|
||||
lint.primary_message(format!(
|
||||
"call to `#[inline(always)]`-annotated `{callee}` \
|
||||
requires the same target features to be inlined"
|
||||
));
|
||||
lint.note("function will not be inlined");
|
||||
|
||||
lint.note(format!(
|
||||
"the following target features are on `{callee}` but missing from `{caller}`: {}",
|
||||
callee_only.join(", ")
|
||||
));
|
||||
lint.span_note(callee_def_id.default_span(tcx), format!("`{callee}` is defined here"));
|
||||
|
||||
let feats = callee_only.join(",");
|
||||
lint.span_suggestion(
|
||||
tcx.def_span(caller_def_id).shrink_to_lo(),
|
||||
format!("add `#[target_feature]` attribute to `{caller}`"),
|
||||
format!("#[target_feature(enable = \"{feats}\")]\n"),
|
||||
lint::Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_unconditional_recursion)]
|
||||
#[help]
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ declare_passes! {
|
|||
mod add_subtyping_projections : Subtyper;
|
||||
mod check_inline : CheckForceInline;
|
||||
mod check_call_recursion : CheckCallRecursion, CheckDropRecursion;
|
||||
mod check_inline_always_target_features: CheckInlineAlwaysTargetFeature;
|
||||
mod check_alignment : CheckAlignment;
|
||||
mod check_enums : CheckEnums;
|
||||
mod check_const_item_mutation : CheckConstItemMutation;
|
||||
|
|
@ -384,6 +385,9 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
|
|||
// MIR-level lints.
|
||||
&Lint(check_inline::CheckForceInline),
|
||||
&Lint(check_call_recursion::CheckCallRecursion),
|
||||
// Check callee's target features match callers target features when
|
||||
// using `#[inline(always)]`
|
||||
&Lint(check_inline_always_target_features::CheckInlineAlwaysTargetFeature),
|
||||
&Lint(check_packed_ref::CheckPackedRef),
|
||||
&Lint(check_const_item_mutation::CheckConstItemMutation),
|
||||
&Lint(function_item_references::FunctionItemReferences),
|
||||
|
|
|
|||
|
|
@ -1275,7 +1275,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
|
|||
>(
|
||||
cx: I,
|
||||
canonical_goal: CanonicalInput<I>,
|
||||
) -> (QueryResult<I>, I::ProbeRef) {
|
||||
) -> (QueryResult<I>, I::Probe) {
|
||||
let mut inspect = inspect::ProofTreeBuilder::new();
|
||||
let canonical_result = SearchGraph::<D>::evaluate_root_goal_for_proof_tree(
|
||||
cx,
|
||||
|
|
@ -1284,7 +1284,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
|
|||
&mut inspect,
|
||||
);
|
||||
let final_revision = inspect.unwrap();
|
||||
(canonical_result, cx.mk_probe_ref(final_revision))
|
||||
(canonical_result, cx.mk_probe(final_revision))
|
||||
}
|
||||
|
||||
/// Evaluate a goal to build a proof tree.
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ parse_async_use_order_incorrect = the order of `use` and `async` is incorrect
|
|||
parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns
|
||||
.suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @`
|
||||
|
||||
parse_at_in_struct_pattern = Unexpected `@` in struct pattern
|
||||
parse_at_in_struct_pattern = unexpected `@` in struct pattern
|
||||
.note = struct patterns use `field: pattern` syntax to bind to fields
|
||||
.help = consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,20 @@ pub fn new_parser_from_source_str(
|
|||
source: String,
|
||||
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
|
||||
let source_file = psess.source_map().new_source_file(name, source);
|
||||
new_parser_from_source_file(psess, source_file)
|
||||
new_parser_from_source_file(psess, source_file, FrontmatterAllowed::Yes)
|
||||
}
|
||||
|
||||
/// Creates a new parser from a simple (no frontmatter) source string.
|
||||
///
|
||||
/// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`,
|
||||
/// etc., otherwise a panic will occur when they are dropped.
|
||||
pub fn new_parser_from_simple_source_str(
|
||||
psess: &ParseSess,
|
||||
name: FileName,
|
||||
source: String,
|
||||
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
|
||||
let source_file = psess.source_map().new_source_file(name, source);
|
||||
new_parser_from_source_file(psess, source_file, FrontmatterAllowed::No)
|
||||
}
|
||||
|
||||
/// Creates a new parser from a filename. On failure, the errors must be consumed via
|
||||
|
|
@ -96,7 +109,7 @@ pub fn new_parser_from_file<'a>(
|
|||
}
|
||||
err.emit();
|
||||
});
|
||||
new_parser_from_source_file(psess, source_file)
|
||||
new_parser_from_source_file(psess, source_file, FrontmatterAllowed::Yes)
|
||||
}
|
||||
|
||||
pub fn utf8_error<E: EmissionGuarantee>(
|
||||
|
|
@ -147,9 +160,10 @@ pub fn utf8_error<E: EmissionGuarantee>(
|
|||
fn new_parser_from_source_file(
|
||||
psess: &ParseSess,
|
||||
source_file: Arc<SourceFile>,
|
||||
frontmatter_allowed: FrontmatterAllowed,
|
||||
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
|
||||
let end_pos = source_file.end_position();
|
||||
let stream = source_file_to_stream(psess, source_file, None, FrontmatterAllowed::Yes)?;
|
||||
let stream = source_file_to_stream(psess, source_file, None, frontmatter_allowed)?;
|
||||
let mut parser = Parser::new(psess, stream, None);
|
||||
if parser.token == token::Eof {
|
||||
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
|
||||
|
|
|
|||
|
|
@ -687,6 +687,7 @@ passes_unused_var_maybe_capture_ref = unused variable: `{$name}`
|
|||
|
||||
passes_unused_var_remove_field = unused variable: `{$name}`
|
||||
passes_unused_var_remove_field_suggestion = try removing the field
|
||||
passes_unused_var_typo = you might have meant to pattern match on the similarly named {$kind} `{$item_name}`
|
||||
|
||||
passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable
|
||||
|
||||
|
|
|
|||
|
|
@ -1367,6 +1367,22 @@ pub(crate) struct UnusedVarRemoveFieldSugg {
|
|||
#[note]
|
||||
pub(crate) struct UnusedVarAssignedOnly {
|
||||
pub name: String,
|
||||
#[subdiagnostic]
|
||||
pub typo: Option<PatternTypo>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
passes_unused_var_typo,
|
||||
style = "verbose",
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
pub(crate) struct PatternTypo {
|
||||
#[suggestion_part(code = "{code}")]
|
||||
pub span: Span,
|
||||
pub code: String,
|
||||
pub item_name: String,
|
||||
pub kind: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
@ -1434,6 +1450,8 @@ pub(crate) struct UnusedVariableTryPrefix {
|
|||
#[subdiagnostic]
|
||||
pub sugg: UnusedVariableSugg,
|
||||
pub name: String,
|
||||
#[subdiagnostic]
|
||||
pub typo: Option<PatternTypo>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
|
|
|||
|
|
@ -95,8 +95,10 @@ use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, find_attr};
|
|||
use rustc_index::IndexVec;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
|
@ -1688,6 +1690,51 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
|||
let is_assigned =
|
||||
if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) };
|
||||
|
||||
let mut typo = None;
|
||||
for (hir_id, _, span) in &hir_ids_and_spans {
|
||||
let ty = self.typeck_results.node_type(*hir_id);
|
||||
if let ty::Adt(adt, _) = ty.peel_refs().kind() {
|
||||
let name = Symbol::intern(&name);
|
||||
let adt_def = self.ir.tcx.adt_def(adt.did());
|
||||
let variant_names: Vec<_> = adt_def
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|v| matches!(v.ctor, Some((CtorKind::Const, _))))
|
||||
.map(|v| v.name)
|
||||
.collect();
|
||||
if let Some(name) = find_best_match_for_name(&variant_names, name, None)
|
||||
&& let Some(variant) = adt_def.variants().iter().find(|v| {
|
||||
v.name == name && matches!(v.ctor, Some((CtorKind::Const, _)))
|
||||
})
|
||||
{
|
||||
typo = Some(errors::PatternTypo {
|
||||
span: *span,
|
||||
code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(variant.def_id)),
|
||||
kind: self.ir.tcx.def_descr(variant.def_id).to_string(),
|
||||
item_name: variant.name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if typo.is_none() {
|
||||
for (hir_id, _, span) in &hir_ids_and_spans {
|
||||
let ty = self.typeck_results.node_type(*hir_id);
|
||||
// Look for consts of the same type with similar names as well, not just unit
|
||||
// structs and variants.
|
||||
for def_id in self.ir.tcx.hir_body_owners() {
|
||||
if let DefKind::Const = self.ir.tcx.def_kind(def_id)
|
||||
&& self.ir.tcx.type_of(def_id).instantiate_identity() == ty
|
||||
{
|
||||
typo = Some(errors::PatternTypo {
|
||||
span: *span,
|
||||
code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(def_id)),
|
||||
kind: "constant".to_string(),
|
||||
item_name: self.ir.tcx.item_name(def_id).to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_assigned {
|
||||
self.ir.tcx.emit_node_span_lint(
|
||||
lint::builtin::UNUSED_VARIABLES,
|
||||
|
|
@ -1696,7 +1743,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
|||
.into_iter()
|
||||
.map(|(_, _, ident_span)| ident_span)
|
||||
.collect::<Vec<_>>(),
|
||||
errors::UnusedVarAssignedOnly { name },
|
||||
errors::UnusedVarAssignedOnly { name, typo },
|
||||
)
|
||||
} else if can_remove {
|
||||
let spans = hir_ids_and_spans
|
||||
|
|
@ -1788,6 +1835,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
|||
name,
|
||||
sugg,
|
||||
string_interp: suggestions,
|
||||
typo,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ query_system_cycle_usage = cycle used when {$usage}
|
|||
query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node}
|
||||
.help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile
|
||||
|
||||
query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information
|
||||
query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information
|
||||
query_system_increment_compilation_note1 = please follow the instructions below to create a bug report with the provided information
|
||||
query_system_increment_compilation_note2 = see <https://github.com/rust-lang/rust/issues/84970> for more information
|
||||
|
||||
query_system_overflow_note = query depth increased by {$depth} when {$desc}
|
||||
|
||||
|
|
|
|||
|
|
@ -470,6 +470,8 @@ resolve_variable_bound_with_different_mode =
|
|||
.label = bound in different ways
|
||||
.first_binding_span = first binding
|
||||
|
||||
resolve_variable_is_a_typo = you might have meant to use the similarly named previously used binding `{$typo}`
|
||||
|
||||
resolve_variable_is_not_bound_in_all_patterns =
|
||||
variable `{$name}` is not bound in all patterns
|
||||
|
||||
|
|
|
|||
|
|
@ -661,8 +661,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
|
||||
let BindingError { name, target, origin, could_be_path } = binding_error;
|
||||
|
||||
let target_sp = target.iter().copied().collect::<Vec<_>>();
|
||||
let origin_sp = origin.iter().copied().collect::<Vec<_>>();
|
||||
let mut target_sp = target.iter().map(|pat| pat.span).collect::<Vec<_>>();
|
||||
target_sp.sort();
|
||||
target_sp.dedup();
|
||||
let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::<Vec<_>>();
|
||||
origin_sp.sort();
|
||||
origin_sp.dedup();
|
||||
|
||||
let msp = MultiSpan::from_spans(target_sp.clone());
|
||||
let mut err = self
|
||||
|
|
@ -671,8 +675,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
for sp in target_sp {
|
||||
err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name });
|
||||
}
|
||||
for sp in origin_sp {
|
||||
err.subdiagnostic(errors::VariableNotInAllPatterns { span: sp });
|
||||
for sp in &origin_sp {
|
||||
err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp });
|
||||
}
|
||||
let mut suggested_typo = false;
|
||||
if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..)))
|
||||
&& !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..)))
|
||||
{
|
||||
// The check above is so that when we encounter `match foo { (a | b) => {} }`,
|
||||
// we don't suggest `(a | a) => {}`, which would never be what the user wants.
|
||||
let mut target_visitor = BindingVisitor::default();
|
||||
for pat in &target {
|
||||
target_visitor.visit_pat(pat);
|
||||
}
|
||||
target_visitor.identifiers.sort();
|
||||
target_visitor.identifiers.dedup();
|
||||
let mut origin_visitor = BindingVisitor::default();
|
||||
for (_, pat) in &origin {
|
||||
origin_visitor.visit_pat(pat);
|
||||
}
|
||||
origin_visitor.identifiers.sort();
|
||||
origin_visitor.identifiers.dedup();
|
||||
// Find if the binding could have been a typo
|
||||
if let Some(typo) =
|
||||
find_best_match_for_name(&target_visitor.identifiers, name.name, None)
|
||||
&& !origin_visitor.identifiers.contains(&typo)
|
||||
{
|
||||
err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo });
|
||||
suggested_typo = true;
|
||||
}
|
||||
}
|
||||
if could_be_path {
|
||||
let import_suggestions = self.lookup_import_candidates(
|
||||
|
|
@ -693,10 +724,86 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
},
|
||||
);
|
||||
|
||||
if import_suggestions.is_empty() {
|
||||
if import_suggestions.is_empty() && !suggested_typo {
|
||||
let kinds = [
|
||||
DefKind::Ctor(CtorOf::Variant, CtorKind::Const),
|
||||
DefKind::Ctor(CtorOf::Struct, CtorKind::Const),
|
||||
DefKind::Const,
|
||||
DefKind::AssocConst,
|
||||
];
|
||||
let mut local_names = vec![];
|
||||
self.add_module_candidates(
|
||||
parent_scope.module,
|
||||
&mut local_names,
|
||||
&|res| matches!(res, Res::Def(_, _)),
|
||||
None,
|
||||
);
|
||||
let local_names: FxHashSet<_> = local_names
|
||||
.into_iter()
|
||||
.filter_map(|s| match s.res {
|
||||
Res::Def(_, def_id) => Some(def_id),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut local_suggestions = vec![];
|
||||
let mut suggestions = vec![];
|
||||
for kind in kinds {
|
||||
if let Some(suggestion) = self.early_lookup_typo_candidate(
|
||||
ScopeSet::All(Namespace::ValueNS),
|
||||
&parent_scope,
|
||||
name,
|
||||
&|res: Res| match res {
|
||||
Res::Def(k, _) => k == kind,
|
||||
_ => false,
|
||||
},
|
||||
) && let Res::Def(kind, mut def_id) = suggestion.res
|
||||
{
|
||||
if let DefKind::Ctor(_, _) = kind {
|
||||
def_id = self.tcx.parent(def_id);
|
||||
}
|
||||
let kind = kind.descr(def_id);
|
||||
if local_names.contains(&def_id) {
|
||||
// The item is available in the current scope. Very likely to
|
||||
// be a typo. Don't use the full path.
|
||||
local_suggestions.push((
|
||||
suggestion.candidate,
|
||||
suggestion.candidate.to_string(),
|
||||
kind,
|
||||
));
|
||||
} else {
|
||||
suggestions.push((
|
||||
suggestion.candidate,
|
||||
self.def_path_str(def_id),
|
||||
kind,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let suggestions = if !local_suggestions.is_empty() {
|
||||
// There is at least one item available in the current scope that is a
|
||||
// likely typo. We only show those.
|
||||
local_suggestions
|
||||
} else {
|
||||
suggestions
|
||||
};
|
||||
for (name, sugg, kind) in suggestions {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!(
|
||||
"you might have meant to use the similarly named {kind} `{name}`",
|
||||
),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
suggested_typo = true;
|
||||
}
|
||||
}
|
||||
if import_suggestions.is_empty() && !suggested_typo {
|
||||
let help_msg = format!(
|
||||
"if you meant to match on a variant or a `const` item, consider \
|
||||
making the path in the pattern qualified: `path::to::ModOrType::{name}`",
|
||||
"if you meant to match on a unit struct, unit variant or a `const` \
|
||||
item, consider making the path in the pattern qualified: \
|
||||
`path::to::ModOrType::{name}`",
|
||||
);
|
||||
err.span_help(span, help_msg);
|
||||
}
|
||||
|
|
@ -1016,6 +1123,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
.emit()
|
||||
}
|
||||
|
||||
fn def_path_str(&self, mut def_id: DefId) -> String {
|
||||
// We can't use `def_path_str` in resolve.
|
||||
let mut path = vec![def_id];
|
||||
while let Some(parent) = self.tcx.opt_parent(def_id) {
|
||||
def_id = parent;
|
||||
path.push(def_id);
|
||||
if def_id.is_top_level_module() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We will only suggest importing directly if it is accessible through that path.
|
||||
path.into_iter()
|
||||
.rev()
|
||||
.map(|def_id| {
|
||||
self.tcx
|
||||
.opt_item_name(def_id)
|
||||
.map(|name| {
|
||||
match (
|
||||
def_id.is_top_level_module(),
|
||||
def_id.is_local(),
|
||||
self.tcx.sess.edition(),
|
||||
) {
|
||||
(true, true, Edition::Edition2015) => String::new(),
|
||||
(true, true, _) => kw::Crate.to_string(),
|
||||
(true, false, _) | (false, _, _) => name.to_string(),
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "_".to_string())
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("::")
|
||||
}
|
||||
|
||||
pub(crate) fn add_scope_set_candidates(
|
||||
&mut self,
|
||||
suggestions: &mut Vec<TypoSuggestion>,
|
||||
|
|
@ -3396,7 +3536,7 @@ impl UsePlacementFinder {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
|
||||
impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
|
||||
fn visit_crate(&mut self, c: &Crate) {
|
||||
if self.target_module == CRATE_NODE_ID {
|
||||
let inject = c.spans.inject_use_span;
|
||||
|
|
@ -3424,6 +3564,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct BindingVisitor {
|
||||
identifiers: Vec<Symbol>,
|
||||
spans: FxHashMap<Symbol, Vec<Span>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BindingVisitor {
|
||||
fn visit_pat(&mut self, pat: &ast::Pat) {
|
||||
if let ast::PatKind::Ident(_, ident, _) = pat.kind {
|
||||
self.identifiers.push(ident.name);
|
||||
self.spans.entry(ident.name).or_default().push(ident.span);
|
||||
}
|
||||
visit::walk_pat(self, pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn search_for_any_use_in_items(items: &[Box<ast::Item>]) -> Option<Span> {
|
||||
for item in items {
|
||||
if let ItemKind::Use(..) = item.kind
|
||||
|
|
|
|||
|
|
@ -986,6 +986,18 @@ pub(crate) struct VariableNotInAllPatterns {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
resolve_variable_is_a_typo,
|
||||
applicability = "maybe-incorrect",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub(crate) struct PatternBindingTypo {
|
||||
#[suggestion_part(code = "{typo}")]
|
||||
pub(crate) spans: Vec<Span>,
|
||||
pub(crate) typo: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_name_defined_multiple_time)]
|
||||
#[note]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::mem::{replace, swap, take};
|
||||
|
||||
|
|
@ -3682,31 +3681,30 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
// 2) Record any missing bindings or binding mode inconsistencies.
|
||||
for (map_outer, pat_outer) in not_never_pats.iter() {
|
||||
// Check against all arms except for the same pattern which is always self-consistent.
|
||||
let inners = not_never_pats
|
||||
.iter()
|
||||
.filter(|(_, pat)| pat.id != pat_outer.id)
|
||||
.flat_map(|(map, _)| map);
|
||||
let inners = not_never_pats.iter().filter(|(_, pat)| pat.id != pat_outer.id);
|
||||
|
||||
for (&name, binding_inner) in inners {
|
||||
match map_outer.get(&name) {
|
||||
None => {
|
||||
// The inner binding is missing in the outer.
|
||||
let binding_error =
|
||||
missing_vars.entry(name).or_insert_with(|| BindingError {
|
||||
name,
|
||||
origin: BTreeSet::new(),
|
||||
target: BTreeSet::new(),
|
||||
could_be_path: name.as_str().starts_with(char::is_uppercase),
|
||||
});
|
||||
binding_error.origin.insert(binding_inner.span);
|
||||
binding_error.target.insert(pat_outer.span);
|
||||
}
|
||||
Some(binding_outer) => {
|
||||
if binding_outer.annotation != binding_inner.annotation {
|
||||
// The binding modes in the outer and inner bindings differ.
|
||||
inconsistent_vars
|
||||
.entry(name)
|
||||
.or_insert((binding_inner.span, binding_outer.span));
|
||||
for (map, pat) in inners {
|
||||
for (&name, binding_inner) in map {
|
||||
match map_outer.get(&name) {
|
||||
None => {
|
||||
// The inner binding is missing in the outer.
|
||||
let binding_error =
|
||||
missing_vars.entry(name).or_insert_with(|| BindingError {
|
||||
name,
|
||||
origin: Default::default(),
|
||||
target: Default::default(),
|
||||
could_be_path: name.as_str().starts_with(char::is_uppercase),
|
||||
});
|
||||
binding_error.origin.push((binding_inner.span, (***pat).clone()));
|
||||
binding_error.target.push((***pat_outer).clone());
|
||||
}
|
||||
Some(binding_outer) => {
|
||||
if binding_outer.annotation != binding_inner.annotation {
|
||||
// The binding modes in the outer and inner bindings differ.
|
||||
inconsistent_vars
|
||||
.entry(name)
|
||||
.or_insert((binding_inner.span, binding_outer.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3719,7 +3717,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
v.could_be_path = false;
|
||||
}
|
||||
self.report_error(
|
||||
*v.origin.iter().next().unwrap(),
|
||||
v.origin.iter().next().unwrap().0,
|
||||
ResolutionError::VariableNotBoundInPattern(v, self.parent_scope),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,8 +230,8 @@ enum Used {
|
|||
#[derive(Debug)]
|
||||
struct BindingError {
|
||||
name: Ident,
|
||||
origin: BTreeSet<Span>,
|
||||
target: BTreeSet<Span>,
|
||||
origin: Vec<(Span, ast::Pat)>,
|
||||
target: Vec<ast::Pat>,
|
||||
could_be_path: bool,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,10 +83,77 @@ pub struct TargetModifier {
|
|||
pub value_name: String,
|
||||
}
|
||||
|
||||
mod target_modifier_consistency_check {
|
||||
use super::*;
|
||||
pub(super) fn sanitizer(l: &TargetModifier, r: Option<&TargetModifier>) -> bool {
|
||||
let mut lparsed: SanitizerSet = Default::default();
|
||||
let lval = if l.value_name.is_empty() { None } else { Some(l.value_name.as_str()) };
|
||||
parse::parse_sanitizers(&mut lparsed, lval);
|
||||
|
||||
let mut rparsed: SanitizerSet = Default::default();
|
||||
let rval = r.filter(|v| !v.value_name.is_empty()).map(|v| v.value_name.as_str());
|
||||
parse::parse_sanitizers(&mut rparsed, rval);
|
||||
|
||||
// Some sanitizers need to be target modifiers, and some do not.
|
||||
// For now, we should mark all sanitizers as target modifiers except for these:
|
||||
// AddressSanitizer, LeakSanitizer
|
||||
let tmod_sanitizers = SanitizerSet::MEMORY
|
||||
| SanitizerSet::THREAD
|
||||
| SanitizerSet::HWADDRESS
|
||||
| SanitizerSet::CFI
|
||||
| SanitizerSet::MEMTAG
|
||||
| SanitizerSet::SHADOWCALLSTACK
|
||||
| SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::SAFESTACK
|
||||
| SanitizerSet::DATAFLOW;
|
||||
|
||||
lparsed & tmod_sanitizers == rparsed & tmod_sanitizers
|
||||
}
|
||||
pub(super) fn sanitizer_cfi_normalize_integers(
|
||||
opts: &Options,
|
||||
l: &TargetModifier,
|
||||
r: Option<&TargetModifier>,
|
||||
) -> bool {
|
||||
// For kCFI, the helper flag -Zsanitizer-cfi-normalize-integers should also be a target modifier
|
||||
if opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) {
|
||||
if let Some(r) = r {
|
||||
return l.extend().tech_value == r.extend().tech_value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetModifier {
|
||||
pub fn extend(&self) -> ExtendedTargetModifierInfo {
|
||||
self.opt.reparse(&self.value_name)
|
||||
}
|
||||
// Custom consistency check for target modifiers (or default `l.tech_value == r.tech_value`)
|
||||
// When other is None, consistency with default value is checked
|
||||
pub fn consistent(&self, opts: &Options, other: Option<&TargetModifier>) -> bool {
|
||||
assert!(other.is_none() || self.opt == other.unwrap().opt);
|
||||
match self.opt {
|
||||
OptionsTargetModifiers::UnstableOptions(unstable) => match unstable {
|
||||
UnstableOptionsTargetModifiers::sanitizer => {
|
||||
return target_modifier_consistency_check::sanitizer(self, other);
|
||||
}
|
||||
UnstableOptionsTargetModifiers::sanitizer_cfi_normalize_integers => {
|
||||
return target_modifier_consistency_check::sanitizer_cfi_normalize_integers(
|
||||
opts, self, other,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
match other {
|
||||
Some(other) => self.extend().tech_value == other.extend().tech_value,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tmod_push_impl(
|
||||
|
|
@ -2504,13 +2571,13 @@ written to standard error output)"),
|
|||
retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
|
||||
"enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \
|
||||
target features (default: no)"),
|
||||
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
|
||||
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER],
|
||||
"use a sanitizer"),
|
||||
sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
|
||||
"enable canonical jump tables (default: yes)"),
|
||||
sanitizer_cfi_generalize_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"enable generalizing pointer types (default: no)"),
|
||||
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED TARGET_MODIFIER],
|
||||
"enable normalizing integer types (default: no)"),
|
||||
sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED],
|
||||
"additional ABI list files that control how shadow parameters are passed (comma separated)"),
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ cfg_select! {
|
|||
// use `loadu`, which supports unaligned loading.
|
||||
let chunk = unsafe { _mm_loadu_si128(chunk.as_ptr() as *const __m128i) };
|
||||
|
||||
// For character in the chunk, see if its byte value is < 0, which
|
||||
// indicates that it's part of a UTF-8 char.
|
||||
// For each character in the chunk, see if its byte value is < 0,
|
||||
// which indicates that it's part of a UTF-8 char.
|
||||
let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0));
|
||||
// Create a bit mask from the comparison results.
|
||||
let multibyte_mask = _mm_movemask_epi8(multibyte_test);
|
||||
|
|
@ -132,8 +132,111 @@ cfg_select! {
|
|||
}
|
||||
}
|
||||
}
|
||||
target_arch = "loongarch64" => {
|
||||
fn analyze_source_file_dispatch(
|
||||
src: &str,
|
||||
lines: &mut Vec<RelativeBytePos>,
|
||||
multi_byte_chars: &mut Vec<MultiByteChar>,
|
||||
) {
|
||||
use std::arch::is_loongarch_feature_detected;
|
||||
|
||||
if is_loongarch_feature_detected!("lsx") {
|
||||
unsafe {
|
||||
analyze_source_file_lsx(src, lines, multi_byte_chars);
|
||||
}
|
||||
} else {
|
||||
analyze_source_file_generic(
|
||||
src,
|
||||
src.len(),
|
||||
RelativeBytePos::from_u32(0),
|
||||
lines,
|
||||
multi_byte_chars,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks 16 byte chunks of text at a time. If the chunk contains
|
||||
/// something other than printable ASCII characters and newlines, the
|
||||
/// function falls back to the generic implementation. Otherwise it uses
|
||||
/// LSX intrinsics to quickly find all newlines.
|
||||
#[target_feature(enable = "lsx")]
|
||||
unsafe fn analyze_source_file_lsx(
|
||||
src: &str,
|
||||
lines: &mut Vec<RelativeBytePos>,
|
||||
multi_byte_chars: &mut Vec<MultiByteChar>,
|
||||
) {
|
||||
use std::arch::loongarch64::*;
|
||||
|
||||
const CHUNK_SIZE: usize = 16;
|
||||
|
||||
let (chunks, tail) = src.as_bytes().as_chunks::<CHUNK_SIZE>();
|
||||
|
||||
// This variable keeps track of where we should start decoding a
|
||||
// chunk. If a multi-byte character spans across chunk boundaries,
|
||||
// we need to skip that part in the next chunk because we already
|
||||
// handled it.
|
||||
let mut intra_chunk_offset = 0;
|
||||
|
||||
for (chunk_index, chunk) in chunks.iter().enumerate() {
|
||||
// All LSX memory instructions support unaligned access, so using
|
||||
// vld is fine.
|
||||
let chunk = unsafe { lsx_vld::<0>(chunk.as_ptr() as *const i8) };
|
||||
|
||||
// For each character in the chunk, see if its byte value is < 0,
|
||||
// which indicates that it's part of a UTF-8 char.
|
||||
let multibyte_mask = lsx_vmskltz_b(chunk);
|
||||
// Create a bit mask from the comparison results.
|
||||
let multibyte_mask = lsx_vpickve2gr_w::<0>(multibyte_mask);
|
||||
|
||||
// If the bit mask is all zero, we only have ASCII chars here:
|
||||
if multibyte_mask == 0 {
|
||||
assert!(intra_chunk_offset == 0);
|
||||
|
||||
// Check for newlines in the chunk
|
||||
let newlines_test = lsx_vseqi_b::<{b'\n' as i32}>(chunk);
|
||||
let newlines_mask = lsx_vmskltz_b(newlines_test);
|
||||
let mut newlines_mask = lsx_vpickve2gr_w::<0>(newlines_mask);
|
||||
|
||||
let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1);
|
||||
|
||||
while newlines_mask != 0 {
|
||||
let index = newlines_mask.trailing_zeros();
|
||||
|
||||
lines.push(RelativeBytePos(index) + output_offset);
|
||||
|
||||
// Clear the bit, so we can find the next one.
|
||||
newlines_mask &= newlines_mask - 1;
|
||||
}
|
||||
} else {
|
||||
// The slow path.
|
||||
// There are multibyte chars in here, fallback to generic decoding.
|
||||
let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset;
|
||||
intra_chunk_offset = analyze_source_file_generic(
|
||||
&src[scan_start..],
|
||||
CHUNK_SIZE - intra_chunk_offset,
|
||||
RelativeBytePos::from_usize(scan_start),
|
||||
lines,
|
||||
multi_byte_chars,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// There might still be a tail left to analyze
|
||||
let tail_start = src.len() - tail.len() + intra_chunk_offset;
|
||||
if tail_start < src.len() {
|
||||
analyze_source_file_generic(
|
||||
&src[tail_start..],
|
||||
src.len() - tail_start,
|
||||
RelativeBytePos::from_usize(tail_start),
|
||||
lines,
|
||||
multi_byte_chars,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// The target (or compiler version) does not support SSE2 ...
|
||||
// The target (or compiler version) does not support vector instructions
|
||||
// our specialized implementations need (x86 SSE2, loongarch64 LSX)...
|
||||
fn analyze_source_file_dispatch(
|
||||
src: &str,
|
||||
lines: &mut Vec<RelativeBytePos>,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(bootstrap, feature(round_char_boundary))]
|
||||
#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(array_windows)]
|
||||
|
|
|
|||
|
|
@ -2162,6 +2162,7 @@ symbols! {
|
|||
target_family,
|
||||
target_feature,
|
||||
target_feature_11,
|
||||
target_feature_inline_always,
|
||||
target_has_atomic,
|
||||
target_has_atomic_equal_alignment,
|
||||
target_has_atomic_load_store,
|
||||
|
|
@ -2280,6 +2281,8 @@ symbols! {
|
|||
unboxed_closures,
|
||||
unchecked_add,
|
||||
unchecked_div,
|
||||
unchecked_funnel_shl,
|
||||
unchecked_funnel_shr,
|
||||
unchecked_mul,
|
||||
unchecked_rem,
|
||||
unchecked_shl,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
|
|||
llvm_target: "aarch64-pc-windows-msvc".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("ARM64 Windows MSVC".into()),
|
||||
tier: Some(2),
|
||||
tier: Some(1),
|
||||
host_tools: Some(true),
|
||||
std: Some(true),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::spec::{
|
|||
pub(crate) fn target() -> Target {
|
||||
Target {
|
||||
arch: "nvptx64".into(),
|
||||
data_layout: "e-p6:32:32-i64:64-i128:128-v16:16-v32:32-n16:32:64".into(),
|
||||
data_layout: "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64".into(),
|
||||
llvm_target: "nvptx64-nvidia-cuda".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("--emit=asm generates PTX code that runs on NVIDIA GPUs".into()),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::ops::Deref;
|
||||
|
|
@ -383,12 +384,12 @@ pub trait Interner:
|
|||
defining_anchor: Self::LocalDefId,
|
||||
) -> Self::LocalDefIds;
|
||||
|
||||
type ProbeRef: Copy + Debug + Hash + Eq + Deref<Target = inspect::Probe<Self>>;
|
||||
fn mk_probe_ref(self, probe: inspect::Probe<Self>) -> Self::ProbeRef;
|
||||
type Probe: Debug + Hash + Eq + Borrow<inspect::Probe<Self>>;
|
||||
fn mk_probe(self, probe: inspect::Probe<Self>) -> Self::Probe;
|
||||
fn evaluate_root_goal_for_proof_tree_raw(
|
||||
self,
|
||||
canonical_goal: CanonicalInput<Self>,
|
||||
) -> (QueryResult<Self>, Self::ProbeRef);
|
||||
) -> (QueryResult<Self>, Self::Probe);
|
||||
}
|
||||
|
||||
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ pub type CanonicalState<I, T> = Canonical<I, State<I, T>>;
|
|||
pub struct GoalEvaluation<I: Interner> {
|
||||
pub uncanonicalized_goal: Goal<I, I::Predicate>,
|
||||
pub orig_values: Vec<I::GenericArg>,
|
||||
pub final_revision: I::ProbeRef,
|
||||
pub final_revision: I::Probe,
|
||||
pub result: QueryResult<I>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -468,10 +468,6 @@ impl<A: Allocator> RawVecInner<A> {
|
|||
return Ok(Self::new_in(alloc, elem_layout.alignment()));
|
||||
}
|
||||
|
||||
if let Err(err) = alloc_guard(layout.size()) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let result = match init {
|
||||
AllocInit::Uninitialized => alloc.allocate(layout),
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
|
|
@ -662,7 +658,7 @@ impl<A: Allocator> RawVecInner<A> {
|
|||
let new_layout = layout_array(cap, elem_layout)?;
|
||||
|
||||
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
|
||||
// SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
|
||||
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
|
||||
|
||||
unsafe { self.set_ptr_and_cap(ptr, cap) };
|
||||
Ok(())
|
||||
|
|
@ -684,7 +680,7 @@ impl<A: Allocator> RawVecInner<A> {
|
|||
let new_layout = layout_array(cap, elem_layout)?;
|
||||
|
||||
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
|
||||
// SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
|
||||
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
|
||||
unsafe {
|
||||
self.set_ptr_and_cap(ptr, cap);
|
||||
}
|
||||
|
|
@ -771,8 +767,6 @@ fn finish_grow<A>(
|
|||
where
|
||||
A: Allocator,
|
||||
{
|
||||
alloc_guard(new_layout.size())?;
|
||||
|
||||
let memory = if let Some((ptr, old_layout)) = current_memory {
|
||||
debug_assert_eq!(old_layout.align(), new_layout.align());
|
||||
unsafe {
|
||||
|
|
@ -799,23 +793,6 @@ fn handle_error(e: TryReserveError) -> ! {
|
|||
}
|
||||
}
|
||||
|
||||
// We need to guarantee the following:
|
||||
// * We don't ever allocate `> isize::MAX` byte-size objects.
|
||||
// * We don't overflow `usize::MAX` and actually allocate too little.
|
||||
//
|
||||
// On 64-bit we just need to check for overflow since trying to allocate
|
||||
// `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add
|
||||
// an extra guard for this in case we're running on a platform which can use
|
||||
// all 4GB in user-space, e.g., PAE or x32.
|
||||
#[inline]
|
||||
fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
|
||||
if usize::BITS < 64 && alloc_size > isize::MAX as usize {
|
||||
Err(CapacityOverflow.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn layout_array(cap: usize, elem_layout: Layout) -> Result<Layout, TryReserveError> {
|
||||
elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into())
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ pub use iter::IntoIter;
|
|||
/// assert_eq!(strings, ["Hello there!", "Hello there!"]);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "cloning is often expensive and is not expected to have side effects"]
|
||||
#[stable(feature = "array_repeat", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn repeat<T: Clone, const N: usize>(val: T) -> [T; N] {
|
||||
from_trusted_iterator(repeat_n(val, N))
|
||||
|
|
|
|||
|
|
@ -148,3 +148,76 @@ impl_disjoint_bitor! {
|
|||
u8, u16, u32, u64, u128, usize,
|
||||
i8, i16, i32, i64, i128, isize,
|
||||
}
|
||||
|
||||
#[const_trait]
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
pub trait FunnelShift: Copy + 'static {
|
||||
/// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle
|
||||
/// different types since calling intrinsics with generics doesn't work.
|
||||
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self;
|
||||
|
||||
/// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle
|
||||
/// different types since calling intrinsics with generics doesn't work.
|
||||
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_funnel_shifts {
|
||||
($($type:ident),*) => {$(
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
impl const FunnelShift for $type {
|
||||
#[cfg_attr(miri, track_caller)]
|
||||
#[inline]
|
||||
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self {
|
||||
// This implementation is also used by Miri so we have to check the precondition.
|
||||
// SAFETY: this is guaranteed by the caller
|
||||
unsafe { super::assume(shift < $type::BITS) };
|
||||
if shift == 0 {
|
||||
self
|
||||
} else {
|
||||
// SAFETY:
|
||||
// - `shift < T::BITS`, which satisfies `unchecked_shl`
|
||||
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
|
||||
// above), which satisfies `unchecked_shr`
|
||||
// - because the types are unsigned, the combination are disjoint bits (this is
|
||||
// not true if they're signed, since SHR will fill in the empty space with a
|
||||
// sign bit, not zero)
|
||||
unsafe {
|
||||
super::disjoint_bitor(
|
||||
super::unchecked_shl(self, shift),
|
||||
super::unchecked_shr(rhs, $type::BITS - shift),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(miri, track_caller)]
|
||||
#[inline]
|
||||
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self {
|
||||
// This implementation is also used by Miri so we have to check the precondition.
|
||||
// SAFETY: this is guaranteed by the caller
|
||||
unsafe { super::assume(shift < $type::BITS) };
|
||||
if shift == 0 {
|
||||
rhs
|
||||
} else {
|
||||
// SAFETY:
|
||||
// - `shift < T::BITS`, which satisfies `unchecked_shr`
|
||||
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
|
||||
// above), which satisfies `unchecked_shl`
|
||||
// - because the types are unsigned, the combination are disjoint bits (this is
|
||||
// not true if they're signed, since SHR will fill in the empty space with a
|
||||
// sign bit, not zero)
|
||||
unsafe {
|
||||
super::disjoint_bitor(
|
||||
super::unchecked_shl(self, $type::BITS - shift),
|
||||
super::unchecked_shr(rhs, shift),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
impl_funnel_shifts! {
|
||||
u8, u16, u32, u64, u128, usize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2102,6 +2102,61 @@ pub const fn saturating_add<T: Copy>(a: T, b: T) -> T;
|
|||
#[rustc_intrinsic]
|
||||
pub const fn saturating_sub<T: Copy>(a: T, b: T) -> T;
|
||||
|
||||
/// Funnel Shift left.
|
||||
///
|
||||
/// Concatenates `a` and `b` (with `a` in the most significant half),
|
||||
/// creating an integer twice as wide. Then shift this integer left
|
||||
/// by `shift`), and extract the most significant half. If `a` and `b`
|
||||
/// are the same, this is equivalent to a rotate left operation.
|
||||
///
|
||||
/// It is undefined behavior if `shift` is greater than or equal to the
|
||||
/// bit size of `T`.
|
||||
///
|
||||
/// Safe versions of this intrinsic are available on the integer primitives
|
||||
/// via the `funnel_shl` method. For example, [`u32::funnel_shl`].
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[track_caller]
|
||||
#[miri::intrinsic_fallback_is_spec]
|
||||
pub const unsafe fn unchecked_funnel_shl<T: [const] fallback::FunnelShift>(
|
||||
a: T,
|
||||
b: T,
|
||||
shift: u32,
|
||||
) -> T {
|
||||
// SAFETY: caller ensures that `shift` is in-range
|
||||
unsafe { a.unchecked_funnel_shl(b, shift) }
|
||||
}
|
||||
|
||||
/// Funnel Shift right.
|
||||
///
|
||||
/// Concatenates `a` and `b` (with `a` in the most significant half),
|
||||
/// creating an integer twice as wide. Then shift this integer right
|
||||
/// by `shift` (taken modulo the bit size of `T`), and extract the
|
||||
/// least significant half. If `a` and `b` are the same, this is equivalent
|
||||
/// to a rotate right operation.
|
||||
///
|
||||
/// It is undefined behavior if `shift` is greater than or equal to the
|
||||
/// bit size of `T`.
|
||||
///
|
||||
/// Safer versions of this intrinsic are available on the integer primitives
|
||||
/// via the `funnel_shr` method. For example, [`u32::funnel_shr`]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[track_caller]
|
||||
#[miri::intrinsic_fallback_is_spec]
|
||||
pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
|
||||
a: T,
|
||||
b: T,
|
||||
shift: u32,
|
||||
) -> T {
|
||||
// SAFETY: caller ensures that `shift` is in-range
|
||||
unsafe { a.unchecked_funnel_shr(b, shift) }
|
||||
}
|
||||
|
||||
/// This is an implementation detail of [`crate::ptr::read`] and should
|
||||
/// not be used anywhere else. See its comments for why this exists.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ impl<A: Default, B: Default> Default for Chain<A, B> {
|
|||
///
|
||||
/// // take requires `Default`
|
||||
/// let _: Chain<_, _> = mem::take(&mut foo.0);
|
||||
/// ```
|
||||
fn default() -> Self {
|
||||
Chain::new(Default::default(), Default::default())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,6 +317,108 @@ impl<I: Iterator> Peekable<I> {
|
|||
{
|
||||
self.next_if(|next| next == expected)
|
||||
}
|
||||
|
||||
/// Consumes the next value of this iterator and applies a function `f` on it,
|
||||
/// returning the result if the closure returns `Ok`.
|
||||
///
|
||||
/// Otherwise if the closure returns `Err` the value is put back for the next iteration.
|
||||
///
|
||||
/// The content of the `Err` variant is typically the original value of the closure,
|
||||
/// but this is not required. If a different value is returned,
|
||||
/// the next `peek()` or `next()` call will result in this new value.
|
||||
/// This is similar to modifying the output of `peek_mut()`.
|
||||
///
|
||||
/// If the closure panics, the next value will always be consumed and dropped
|
||||
/// even if the panic is caught, because the closure never returned an `Err` value to put back.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Parse the leading decimal number from an iterator of characters.
|
||||
/// ```
|
||||
/// #![feature(peekable_next_if_map)]
|
||||
/// let mut iter = "125 GOTO 10".chars().peekable();
|
||||
/// let mut line_num = 0_u32;
|
||||
/// while let Some(digit) = iter.next_if_map(|c| c.to_digit(10).ok_or(c)) {
|
||||
/// line_num = line_num * 10 + digit;
|
||||
/// }
|
||||
/// assert_eq!(line_num, 125);
|
||||
/// assert_eq!(iter.collect::<String>(), " GOTO 10");
|
||||
/// ```
|
||||
///
|
||||
/// Matching custom types.
|
||||
/// ```
|
||||
/// #![feature(peekable_next_if_map)]
|
||||
///
|
||||
/// #[derive(Debug, PartialEq, Eq)]
|
||||
/// enum Node {
|
||||
/// Comment(String),
|
||||
/// Red(String),
|
||||
/// Green(String),
|
||||
/// Blue(String),
|
||||
/// }
|
||||
///
|
||||
/// /// Combines all consecutive `Comment` nodes into a single one.
|
||||
/// fn combine_comments(nodes: Vec<Node>) -> Vec<Node> {
|
||||
/// let mut result = Vec::with_capacity(nodes.len());
|
||||
/// let mut iter = nodes.into_iter().peekable();
|
||||
/// let mut comment_text = None::<String>;
|
||||
/// loop {
|
||||
/// // Typically the closure in .next_if_map() matches on the input,
|
||||
/// // extracts the desired pattern into an `Ok`,
|
||||
/// // and puts the rest into an `Err`.
|
||||
/// while let Some(text) = iter.next_if_map(|node| match node {
|
||||
/// Node::Comment(text) => Ok(text),
|
||||
/// other => Err(other),
|
||||
/// }) {
|
||||
/// comment_text.get_or_insert_default().push_str(&text);
|
||||
/// }
|
||||
///
|
||||
/// if let Some(text) = comment_text.take() {
|
||||
/// result.push(Node::Comment(text));
|
||||
/// }
|
||||
/// if let Some(node) = iter.next() {
|
||||
/// result.push(node);
|
||||
/// } else {
|
||||
/// break;
|
||||
/// }
|
||||
/// }
|
||||
/// result
|
||||
/// }
|
||||
///# assert_eq!( // hiding the test to avoid cluttering the documentation.
|
||||
///# combine_comments(vec![
|
||||
///# Node::Comment("The".to_owned()),
|
||||
///# Node::Comment("Quick".to_owned()),
|
||||
///# Node::Comment("Brown".to_owned()),
|
||||
///# Node::Red("Fox".to_owned()),
|
||||
///# Node::Green("Jumped".to_owned()),
|
||||
///# Node::Comment("Over".to_owned()),
|
||||
///# Node::Blue("The".to_owned()),
|
||||
///# Node::Comment("Lazy".to_owned()),
|
||||
///# Node::Comment("Dog".to_owned()),
|
||||
///# ]),
|
||||
///# vec![
|
||||
///# Node::Comment("TheQuickBrown".to_owned()),
|
||||
///# Node::Red("Fox".to_owned()),
|
||||
///# Node::Green("Jumped".to_owned()),
|
||||
///# Node::Comment("Over".to_owned()),
|
||||
///# Node::Blue("The".to_owned()),
|
||||
///# Node::Comment("LazyDog".to_owned()),
|
||||
///# ],
|
||||
///# )
|
||||
/// ```
|
||||
#[unstable(feature = "peekable_next_if_map", issue = "143702")]
|
||||
pub fn next_if_map<R>(&mut self, f: impl FnOnce(I::Item) -> Result<R, I::Item>) -> Option<R> {
|
||||
let unpeek = if let Some(item) = self.next() {
|
||||
match f(item) {
|
||||
Ok(result) => return Some(result),
|
||||
Err(item) => Some(item),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.peeked = Some(unpeek);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@
|
|||
#![feature(f128)]
|
||||
#![feature(freeze_impls)]
|
||||
#![feature(fundamental)]
|
||||
#![feature(funnel_shifts)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(intra_doc_pointers)]
|
||||
#![feature(intrinsics)]
|
||||
|
|
|
|||
|
|
@ -1285,7 +1285,7 @@ macro_rules! int_impl {
|
|||
///
|
||||
/// ```should_panic
|
||||
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_neg();")]
|
||||
///
|
||||
/// ```
|
||||
#[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
|
|
|
|||
|
|
@ -454,6 +454,9 @@ impl u8 {
|
|||
rot = 2,
|
||||
rot_op = "0x82",
|
||||
rot_result = "0xa",
|
||||
fsh_op = "0x36",
|
||||
fshl_result = "0x8",
|
||||
fshr_result = "0x8d",
|
||||
swap_op = "0x12",
|
||||
swapped = "0x12",
|
||||
reversed = "0x48",
|
||||
|
|
@ -1088,6 +1091,9 @@ impl u16 {
|
|||
rot = 4,
|
||||
rot_op = "0xa003",
|
||||
rot_result = "0x3a",
|
||||
fsh_op = "0x2de",
|
||||
fshl_result = "0x30",
|
||||
fshr_result = "0x302d",
|
||||
swap_op = "0x1234",
|
||||
swapped = "0x3412",
|
||||
reversed = "0x2c48",
|
||||
|
|
@ -1135,6 +1141,9 @@ impl u32 {
|
|||
rot = 8,
|
||||
rot_op = "0x10000b3",
|
||||
rot_result = "0xb301",
|
||||
fsh_op = "0x2fe78e45",
|
||||
fshl_result = "0xb32f",
|
||||
fshr_result = "0xb32fe78e",
|
||||
swap_op = "0x12345678",
|
||||
swapped = "0x78563412",
|
||||
reversed = "0x1e6a2c48",
|
||||
|
|
@ -1158,6 +1167,9 @@ impl u64 {
|
|||
rot = 12,
|
||||
rot_op = "0xaa00000000006e1",
|
||||
rot_result = "0x6e10aa",
|
||||
fsh_op = "0x2fe78e45983acd98",
|
||||
fshl_result = "0x6e12fe",
|
||||
fshr_result = "0x6e12fe78e45983ac",
|
||||
swap_op = "0x1234567890123456",
|
||||
swapped = "0x5634129078563412",
|
||||
reversed = "0x6a2c48091e6a2c48",
|
||||
|
|
@ -1181,6 +1193,9 @@ impl u128 {
|
|||
rot = 16,
|
||||
rot_op = "0x13f40000000000000000000000004f76",
|
||||
rot_result = "0x4f7613f4",
|
||||
fsh_op = "0x2fe78e45983acd98039000008736273",
|
||||
fshl_result = "0x4f7602fe",
|
||||
fshr_result = "0x4f7602fe78e45983acd9803900000873",
|
||||
swap_op = "0x12345678901234567890123456789012",
|
||||
swapped = "0x12907856341290785634129078563412",
|
||||
reversed = "0x48091e6a2c48091e6a2c48091e6a2c48",
|
||||
|
|
@ -1207,6 +1222,9 @@ impl usize {
|
|||
rot = 4,
|
||||
rot_op = "0xa003",
|
||||
rot_result = "0x3a",
|
||||
fsh_op = "0x2fe78e45983acd98039000008736273",
|
||||
fshl_result = "0x4f7602fe",
|
||||
fshr_result = "0x4f7602fe78e45983acd9803900000873",
|
||||
swap_op = "0x1234",
|
||||
swapped = "0x3412",
|
||||
reversed = "0x2c48",
|
||||
|
|
@ -1231,6 +1249,9 @@ impl usize {
|
|||
rot = 8,
|
||||
rot_op = "0x10000b3",
|
||||
rot_result = "0xb301",
|
||||
fsh_op = "0x2fe78e45",
|
||||
fshl_result = "0xb32f",
|
||||
fshr_result = "0xb32fe78e",
|
||||
swap_op = "0x12345678",
|
||||
swapped = "0x78563412",
|
||||
reversed = "0x1e6a2c48",
|
||||
|
|
@ -1255,6 +1276,9 @@ impl usize {
|
|||
rot = 12,
|
||||
rot_op = "0xaa00000000006e1",
|
||||
rot_result = "0x6e10aa",
|
||||
fsh_op = "0x2fe78e45983acd98",
|
||||
fshl_result = "0x6e12fe",
|
||||
fshr_result = "0x6e12fe78e45983ac",
|
||||
swap_op = "0x1234567890123456",
|
||||
swapped = "0x5634129078563412",
|
||||
reversed = "0x6a2c48091e6a2c48",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ macro_rules! uint_impl {
|
|||
rot = $rot:literal,
|
||||
rot_op = $rot_op:literal,
|
||||
rot_result = $rot_result:literal,
|
||||
fsh_op = $fsh_op:literal,
|
||||
fshl_result = $fshl_result:literal,
|
||||
fshr_result = $fshr_result:literal,
|
||||
swap_op = $swap_op:literal,
|
||||
swapped = $swapped:literal,
|
||||
reversed = $reversed:literal,
|
||||
|
|
@ -375,6 +378,76 @@ macro_rules! uint_impl {
|
|||
return intrinsics::rotate_right(self, n);
|
||||
}
|
||||
|
||||
/// Performs a left funnel shift (concatenates `self` with `rhs`, with `self`
|
||||
/// making up the most significant half, then shifts the combined value left
|
||||
/// by `n`, and most significant half is extracted to produce the result).
|
||||
///
|
||||
/// Please note this isn't the same operation as the `<<` shifting operator or
|
||||
/// [`rotate_left`](Self::rotate_left), although `a.funnel_shl(a, n)` is *equivalent*
|
||||
/// to `a.rotate_left(n)`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `n` is greater than or equal to the number of bits in `self`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(funnel_shifts)]
|
||||
#[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")]
|
||||
#[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")]
|
||||
#[doc = concat!("let m = ", $fshl_result, ";")]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(a.funnel_shl(b, ", $rot, "), m);")]
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline(always)]
|
||||
pub const fn funnel_shl(self, rhs: Self, n: u32) -> Self {
|
||||
assert!(n < Self::BITS, "attempt to funnel shift left with overflow");
|
||||
// SAFETY: just checked that `shift` is in-range
|
||||
unsafe { intrinsics::unchecked_funnel_shl(self, rhs, n) }
|
||||
}
|
||||
|
||||
/// Performs a right funnel shift (concatenates `self` and `rhs`, with `self`
|
||||
/// making up the most significant half, then shifts the combined value right
|
||||
/// by `n`, and least significant half is extracted to produce the result).
|
||||
///
|
||||
/// Please note this isn't the same operation as the `>>` shifting operator or
|
||||
/// [`rotate_right`](Self::rotate_right), although `a.funnel_shr(a, n)` is *equivalent*
|
||||
/// to `a.rotate_right(n)`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `n` is greater than or equal to the number of bits in `self`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(funnel_shifts)]
|
||||
#[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")]
|
||||
#[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")]
|
||||
#[doc = concat!("let m = ", $fshr_result, ";")]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(a.funnel_shr(b, ", $rot, "), m);")]
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[unstable(feature = "funnel_shifts", issue = "145686")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline(always)]
|
||||
pub const fn funnel_shr(self, rhs: Self, n: u32) -> Self {
|
||||
assert!(n < Self::BITS, "attempt to funnel shift right with overflow");
|
||||
// SAFETY: just checked that `shift` is in-range
|
||||
unsafe { intrinsics::unchecked_funnel_shr(self, rhs, n) }
|
||||
}
|
||||
|
||||
/// Reverses the byte order of the integer.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -1620,7 +1693,7 @@ macro_rules! uint_impl {
|
|||
///
|
||||
/// ```should_panic
|
||||
#[doc = concat!("let _ = 1", stringify!($SelfT), ".strict_neg();")]
|
||||
///
|
||||
/// ```
|
||||
#[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
|
|
|
|||
|
|
@ -836,6 +836,7 @@ pub trait RangeBounds<T: ?Sized> {
|
|||
/// assert!(!(0.0..1.0).contains(&f32::NAN));
|
||||
/// assert!(!(0.0..f32::NAN).contains(&0.5));
|
||||
/// assert!(!(f32::NAN..1.0).contains(&0.5));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "range_contains", since = "1.35.0")]
|
||||
fn contains<U>(&self, item: &U) -> bool
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ impl<T: PointeeSized> *mut T {
|
|||
///
|
||||
/// // This dereference is UB. The pointer only has provenance for `x` but points to `y`.
|
||||
/// println!("{:?}", unsafe { &*bad });
|
||||
/// ```
|
||||
#[unstable(feature = "set_ptr_value", issue = "75091")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -271,3 +271,89 @@ fn test_peekable_non_fused() {
|
|||
assert_eq!(iter.peek(), None);
|
||||
assert_eq!(iter.next_back(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_peekable_next_if_map_mutation() {
|
||||
fn collatz((mut num, mut len): (u64, u32)) -> Result<u32, (u64, u32)> {
|
||||
let jump = num.trailing_zeros();
|
||||
num >>= jump;
|
||||
len += jump;
|
||||
if num == 1 { Ok(len) } else { Err((3 * num + 1, len + 1)) }
|
||||
}
|
||||
|
||||
let mut iter = once((3, 0)).peekable();
|
||||
assert_eq!(iter.peek(), Some(&(3, 0)));
|
||||
assert_eq!(iter.next_if_map(collatz), None);
|
||||
assert_eq!(iter.peek(), Some(&(10, 1)));
|
||||
assert_eq!(iter.next_if_map(collatz), None);
|
||||
assert_eq!(iter.peek(), Some(&(16, 3)));
|
||||
assert_eq!(iter.next_if_map(collatz), Some(7));
|
||||
assert_eq!(iter.peek(), None);
|
||||
assert_eq!(iter.next_if_map(collatz), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_peekable_next_if_map_panic() {
|
||||
use core::cell::Cell;
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
|
||||
struct BitsetOnDrop<'a> {
|
||||
value: u32,
|
||||
cell: &'a Cell<u32>,
|
||||
}
|
||||
impl<'a> Drop for BitsetOnDrop<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.cell.update(|v| v | self.value);
|
||||
}
|
||||
}
|
||||
|
||||
let cell = &Cell::new(0);
|
||||
let mut it = [
|
||||
BitsetOnDrop { value: 1, cell },
|
||||
BitsetOnDrop { value: 2, cell },
|
||||
BitsetOnDrop { value: 4, cell },
|
||||
BitsetOnDrop { value: 8, cell },
|
||||
]
|
||||
.into_iter()
|
||||
.peekable();
|
||||
|
||||
// sanity check, .peek() won't consume the value, .next() will transfer ownership.
|
||||
let item = it.peek().unwrap();
|
||||
assert_eq!(item.value, 1);
|
||||
assert_eq!(cell.get(), 0);
|
||||
let item = it.next().unwrap();
|
||||
assert_eq!(item.value, 1);
|
||||
assert_eq!(cell.get(), 0);
|
||||
drop(item);
|
||||
assert_eq!(cell.get(), 1);
|
||||
|
||||
// next_if_map returning Ok should transfer the value out.
|
||||
let item = it.next_if_map(Ok).unwrap();
|
||||
assert_eq!(item.value, 2);
|
||||
assert_eq!(cell.get(), 1);
|
||||
drop(item);
|
||||
assert_eq!(cell.get(), 3);
|
||||
|
||||
// next_if_map returning Err should not drop anything.
|
||||
assert_eq!(it.next_if_map::<()>(Err), None);
|
||||
assert_eq!(cell.get(), 3);
|
||||
assert_eq!(it.peek().unwrap().value, 4);
|
||||
assert_eq!(cell.get(), 3);
|
||||
|
||||
// next_if_map panicking should consume and drop the item.
|
||||
let result = catch_unwind({
|
||||
let mut it = AssertUnwindSafe(&mut it);
|
||||
move || it.next_if_map::<()>(|_| panic!())
|
||||
});
|
||||
assert!(result.is_err());
|
||||
assert_eq!(cell.get(), 7);
|
||||
assert_eq!(it.next().unwrap().value, 8);
|
||||
assert_eq!(cell.get(), 15);
|
||||
assert!(it.peek().is_none());
|
||||
|
||||
// next_if_map should *not* execute the closure if the iterator is exhausted.
|
||||
assert!(it.next_if_map::<()>(|_| panic!()).is_none());
|
||||
assert!(it.peek().is_none());
|
||||
assert_eq!(cell.get(), 15);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#![feature(fmt_internals)]
|
||||
#![feature(formatting_options)]
|
||||
#![feature(freeze)]
|
||||
#![feature(funnel_shifts)]
|
||||
#![feature(future_join)]
|
||||
#![feature(generic_assert_internals)]
|
||||
#![feature(hasher_prefixfree_extras)]
|
||||
|
|
@ -82,6 +83,7 @@
|
|||
#![feature(numfmt)]
|
||||
#![feature(option_reduce)]
|
||||
#![feature(pattern)]
|
||||
#![feature(peekable_next_if_map)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(ptr_metadata)]
|
||||
|
|
|
|||
|
|
@ -104,6 +104,19 @@ macro_rules! uint_module {
|
|||
assert_eq_const_safe!($T: C.rotate_left(128), C);
|
||||
}
|
||||
|
||||
fn test_funnel_shift() {
|
||||
// Shifting by 0 should have no effect
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shl(A, B, 0), A);
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shr(A, B, 0), B);
|
||||
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shl(_0, _1, 4), 0b1111);
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shr(_0, _1, 4), _1 >> 4);
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shl(_1, _0, 4), _1 << 4);
|
||||
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shl(_1, _1, 4), <$T>::rotate_left(_1, 4));
|
||||
assert_eq_const_safe!($T: <$T>::funnel_shr(_1, _1, 4), <$T>::rotate_right(_1, 4));
|
||||
}
|
||||
|
||||
fn test_swap_bytes() {
|
||||
assert_eq_const_safe!($T: A.swap_bytes().swap_bytes(), A);
|
||||
assert_eq_const_safe!($T: B.swap_bytes().swap_bytes(), B);
|
||||
|
|
@ -150,6 +163,29 @@ macro_rules! uint_module {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "attempt to funnel shift left with overflow"]
|
||||
fn test_funnel_shl_overflow() {
|
||||
let _ = <$T>::funnel_shl(A, B, $T::BITS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "attempt to funnel shift right with overflow"]
|
||||
fn test_funnel_shr_overflow() {
|
||||
let _ = <$T>::funnel_shr(A, B, $T::BITS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_funnel_shifts_runtime() {
|
||||
for i in 0..$T::BITS - 1 {
|
||||
assert_eq!(<$T>::funnel_shl(A, 0, i), A << i);
|
||||
assert_eq!(<$T>::funnel_shl(A, A, i), A.rotate_left(i));
|
||||
|
||||
assert_eq!(<$T>::funnel_shr(0, A, i), A >> i);
|
||||
assert_eq!(<$T>::funnel_shr(A, A, i), A.rotate_right(i));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_isolate_highest_one() {
|
||||
const BITS: $T = <$T>::MAX;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ pub trait SimdInt: Copy + Sealed {
|
|||
/// let sat = x.saturating_sub(max);
|
||||
/// assert_eq!(unsat, Simd::from_array([1, MAX, MIN, 0]));
|
||||
/// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0]));
|
||||
/// ```
|
||||
fn saturating_sub(self, second: Self) -> Self;
|
||||
|
||||
/// Lanewise absolute value, implemented in Rust.
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ pub trait SimdUint: Copy + Sealed {
|
|||
/// let sat = x.saturating_sub(max);
|
||||
/// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0]));
|
||||
/// assert_eq!(sat, Simd::splat(0));
|
||||
/// ```
|
||||
fn saturating_sub(self, second: Self) -> Self;
|
||||
|
||||
/// Lanewise absolute difference.
|
||||
|
|
|
|||
|
|
@ -490,6 +490,85 @@ fn file_test_io_read_write_at() {
|
|||
check!(fs::remove_file(&filename));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_read_buf_at() {
|
||||
use crate::os::unix::fs::FileExt;
|
||||
|
||||
let tmpdir = tmpdir();
|
||||
let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt");
|
||||
{
|
||||
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
|
||||
let mut file = check!(oo.open(&filename));
|
||||
check!(file.write_all(b"0123456789"));
|
||||
}
|
||||
{
|
||||
let mut file = check!(File::open(&filename));
|
||||
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
|
||||
// Fill entire buffer with potentially short reads
|
||||
while buf.unfilled().capacity() > 0 {
|
||||
let len = buf.len();
|
||||
check!(file.read_buf_at(buf.unfilled(), 2 + len as u64));
|
||||
assert!(!buf.filled().is_empty());
|
||||
assert!(b"23456".starts_with(buf.filled()));
|
||||
assert_eq!(check!(file.stream_position()), 0);
|
||||
}
|
||||
assert_eq!(buf.filled(), b"23456");
|
||||
|
||||
// Already full
|
||||
check!(file.read_buf_at(buf.unfilled(), 3));
|
||||
check!(file.read_buf_at(buf.unfilled(), 10));
|
||||
assert_eq!(buf.filled(), b"23456");
|
||||
assert_eq!(check!(file.stream_position()), 0);
|
||||
|
||||
// Read past eof is noop
|
||||
check!(file.read_buf_at(buf.clear().unfilled(), 10));
|
||||
assert_eq!(buf.filled(), b"");
|
||||
check!(file.read_buf_at(buf.clear().unfilled(), 11));
|
||||
assert_eq!(buf.filled(), b"");
|
||||
assert_eq!(check!(file.stream_position()), 0);
|
||||
}
|
||||
check!(fs::remove_file(&filename));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_read_buf_exact_at() {
|
||||
use crate::os::unix::fs::FileExt;
|
||||
|
||||
let tmpdir = tmpdir();
|
||||
let filename = tmpdir.join("file_rt_io_file_test_read_buf_exact_at.txt");
|
||||
{
|
||||
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
|
||||
let mut file = check!(oo.open(&filename));
|
||||
check!(file.write_all(b"0123456789"));
|
||||
}
|
||||
{
|
||||
let mut file = check!(File::open(&filename));
|
||||
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
|
||||
// Exact read
|
||||
check!(file.read_buf_exact_at(buf.unfilled(), 2));
|
||||
assert_eq!(buf.filled(), b"23456");
|
||||
assert_eq!(check!(file.stream_position()), 0);
|
||||
|
||||
// Already full
|
||||
check!(file.read_buf_exact_at(buf.unfilled(), 3));
|
||||
check!(file.read_buf_exact_at(buf.unfilled(), 10));
|
||||
assert_eq!(buf.filled(), b"23456");
|
||||
assert_eq!(check!(file.stream_position()), 0);
|
||||
|
||||
// Non-empty exact read past eof fails
|
||||
let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(check!(file.stream_position()), 0);
|
||||
}
|
||||
check!(fs::remove_file(&filename));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn set_get_unix_permissions() {
|
||||
|
|
@ -566,6 +645,39 @@ fn file_test_io_seek_read_write() {
|
|||
check!(fs::remove_file(&filename));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_seek_read_buf() {
|
||||
use crate::os::windows::fs::FileExt;
|
||||
|
||||
let tmpdir = tmpdir();
|
||||
let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt");
|
||||
{
|
||||
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
|
||||
let mut file = check!(oo.open(&filename));
|
||||
check!(file.write_all(b"0123456789"));
|
||||
}
|
||||
{
|
||||
let mut file = check!(File::open(&filename));
|
||||
let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
|
||||
// Seek read
|
||||
check!(file.seek_read_buf(buf.unfilled(), 8));
|
||||
assert_eq!(buf.filled(), b"8");
|
||||
assert_eq!(check!(file.stream_position()), 9);
|
||||
|
||||
// Empty seek read
|
||||
check!(file.seek_read_buf(buf.unfilled(), 0));
|
||||
assert_eq!(buf.filled(), b"8");
|
||||
|
||||
// Seek read past eof
|
||||
check!(file.seek_read_buf(buf.clear().unfilled(), 10));
|
||||
assert_eq!(buf.filled(), b"");
|
||||
}
|
||||
check!(fs::remove_file(&filename));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_test_read_buf() {
|
||||
let tmpdir = tmpdir();
|
||||
|
|
|
|||
|
|
@ -294,6 +294,7 @@
|
|||
#![feature(f128)]
|
||||
#![feature(ffi_const)]
|
||||
#![feature(formatting_options)]
|
||||
#![feature(funnel_shifts)]
|
||||
#![feature(hash_map_internals)]
|
||||
#![feature(hash_map_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use super::platform::fs::MetadataExt as _;
|
|||
// Used for `File::read` on intra-doc links
|
||||
use crate::ffi::OsStr;
|
||||
use crate::fs::{self, OpenOptions, Permissions};
|
||||
use crate::io::BorrowedCursor;
|
||||
use crate::os::unix::io::{AsFd, AsRawFd};
|
||||
use crate::path::Path;
|
||||
use crate::sealed::Sealed;
|
||||
|
|
@ -130,6 +131,91 @@ pub trait FileExt {
|
|||
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
/// Reads some bytes starting from a given offset into the buffer.
|
||||
///
|
||||
/// This equivalent to the [`read_at`](FileExt::read_at) method, except that it is passed a
|
||||
/// [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The new
|
||||
/// data will be appended to any existing contents of `buf`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(core_io_borrowed_buf)]
|
||||
/// #![feature(read_buf_at)]
|
||||
///
|
||||
/// use std::io;
|
||||
/// use std::io::BorrowedBuf;
|
||||
/// use std::fs::File;
|
||||
/// use std::mem::MaybeUninit;
|
||||
/// use std::os::unix::prelude::*;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut file = File::open("pi.txt")?;
|
||||
///
|
||||
/// // Read some bytes starting from offset 2
|
||||
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
|
||||
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
/// file.read_buf_at(buf.unfilled(), 2)?;
|
||||
///
|
||||
/// assert!(buf.filled().starts_with(b"1"));
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "read_buf_at", issue = "140771")]
|
||||
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
io::default_read_buf(|b| self.read_at(b, offset), buf)
|
||||
}
|
||||
|
||||
/// Reads the exact number of bytes required to fill the buffer from a given offset.
|
||||
///
|
||||
/// This is equivalent to the [`read_exact_at`](FileExt::read_exact_at) method, except that it
|
||||
/// is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized
|
||||
/// buffers. The new data will be appended to any existing contents of `buf`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(core_io_borrowed_buf)]
|
||||
/// #![feature(read_buf_at)]
|
||||
///
|
||||
/// use std::io;
|
||||
/// use std::io::BorrowedBuf;
|
||||
/// use std::fs::File;
|
||||
/// use std::mem::MaybeUninit;
|
||||
/// use std::os::unix::prelude::*;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut file = File::open("pi.txt")?;
|
||||
///
|
||||
/// // Read exactly 10 bytes starting from offset 2
|
||||
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
|
||||
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
/// file.read_buf_exact_at(buf.unfilled(), 2)?;
|
||||
///
|
||||
/// assert_eq!(buf.filled(), b"1415926535");
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "read_buf_at", issue = "140771")]
|
||||
fn read_buf_exact_at(&self, mut buf: BorrowedCursor<'_>, mut offset: u64) -> io::Result<()> {
|
||||
while buf.capacity() > 0 {
|
||||
let prev_written = buf.written();
|
||||
match self.read_buf_at(buf.reborrow(), offset) {
|
||||
Ok(()) => {}
|
||||
Err(e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let n = buf.written() - prev_written;
|
||||
offset += n as u64;
|
||||
if n == 0 {
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a number of bytes starting from a given offset.
|
||||
///
|
||||
/// Returns the number of bytes written.
|
||||
|
|
@ -264,6 +350,9 @@ impl FileExt for fs::File {
|
|||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().read_at(buf, offset)
|
||||
}
|
||||
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
self.as_inner().read_buf_at(buf, offset)
|
||||
}
|
||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().read_vectored_at(bufs, offset)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::fs::{self, Metadata, OpenOptions};
|
||||
use crate::io::BorrowedCursor;
|
||||
use crate::path::Path;
|
||||
use crate::sealed::Sealed;
|
||||
use crate::sys_common::{AsInner, AsInnerMut, IntoInner};
|
||||
|
|
@ -49,6 +50,44 @@ pub trait FileExt {
|
|||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
|
||||
|
||||
/// Seeks to a given position and reads some bytes into the buffer.
|
||||
///
|
||||
/// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except that it is passed
|
||||
/// a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The
|
||||
/// new data will be appended to any existing contents of `buf`.
|
||||
///
|
||||
/// Reading beyond the end of the file will always succeed without reading any bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(core_io_borrowed_buf)]
|
||||
/// #![feature(read_buf_at)]
|
||||
///
|
||||
/// use std::io;
|
||||
/// use std::io::BorrowedBuf;
|
||||
/// use std::fs::File;
|
||||
/// use std::mem::MaybeUninit;
|
||||
/// use std::os::windows::prelude::*;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut file = File::open("pi.txt")?;
|
||||
///
|
||||
/// // Read some bytes starting from offset 2
|
||||
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
|
||||
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
/// file.seek_read_buf(buf.unfilled(), 2)?;
|
||||
///
|
||||
/// assert!(buf.filled().starts_with(b"1"));
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "read_buf_at", issue = "140771")]
|
||||
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
io::default_read_buf(|b| self.seek_read(b, offset), buf)
|
||||
}
|
||||
|
||||
/// Seeks to a given position and writes a number of bytes.
|
||||
///
|
||||
/// Returns the number of bytes written.
|
||||
|
|
@ -89,6 +128,10 @@ impl FileExt for fs::File {
|
|||
self.as_inner().read_at(buf, offset)
|
||||
}
|
||||
|
||||
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
self.as_inner().read_buf_at(buf, offset)
|
||||
}
|
||||
|
||||
fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().write_at(buf, offset)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,8 +1575,6 @@ impl PathBuf {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(path_add_extension)]
|
||||
///
|
||||
/// use std::path::{Path, PathBuf};
|
||||
///
|
||||
/// let mut p = PathBuf::from("/feel/the");
|
||||
|
|
@ -1596,7 +1594,7 @@ impl PathBuf {
|
|||
/// p.add_extension("");
|
||||
/// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path());
|
||||
/// ```
|
||||
#[unstable(feature = "path_add_extension", issue = "127292")]
|
||||
#[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn add_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
|
||||
self._add_extension(extension.as_ref())
|
||||
}
|
||||
|
|
@ -2109,7 +2107,7 @@ impl PartialEq for PathBuf {
|
|||
impl cmp::PartialEq<str> for PathBuf {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
&*self == other
|
||||
Path::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2880,8 +2878,6 @@ impl Path {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(path_add_extension)]
|
||||
///
|
||||
/// use std::path::{Path, PathBuf};
|
||||
///
|
||||
/// let path = Path::new("foo.rs");
|
||||
|
|
@ -2892,7 +2888,7 @@ impl Path {
|
|||
/// assert_eq!(path.with_added_extension("xz"), PathBuf::from("foo.tar.gz.xz"));
|
||||
/// assert_eq!(path.with_added_extension("").with_added_extension("txt"), PathBuf::from("foo.tar.gz.txt"));
|
||||
/// ```
|
||||
#[unstable(feature = "path_add_extension", issue = "127292")]
|
||||
#[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn with_added_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
|
||||
let mut new_path = self.to_path_buf();
|
||||
new_path.add_extension(extension);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,21 @@ use libc::off_t as off64_t;
|
|||
))]
|
||||
use libc::off64_t;
|
||||
|
||||
cfg_select! {
|
||||
any(
|
||||
all(target_os = "linux", not(target_env = "musl")),
|
||||
target_os = "android",
|
||||
target_os = "hurd",
|
||||
) => {
|
||||
// Prefer explicit pread64 for 64-bit offset independently of libc
|
||||
// #[cfg(gnu_file_offset_bits64)].
|
||||
use libc::pread64;
|
||||
}
|
||||
_ => {
|
||||
use libc::pread as pread64;
|
||||
}
|
||||
}
|
||||
|
||||
use crate::cmp;
|
||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
|
||||
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
|
|
@ -146,42 +161,47 @@ impl FileDesc {
|
|||
(&mut me).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
|
||||
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
#[cfg(not(any(
|
||||
all(target_os = "linux", not(target_env = "musl")),
|
||||
target_os = "android",
|
||||
target_os = "hurd"
|
||||
)))]
|
||||
use libc::pread as pread64;
|
||||
#[cfg(any(
|
||||
all(target_os = "linux", not(target_env = "musl")),
|
||||
target_os = "android",
|
||||
target_os = "hurd"
|
||||
))]
|
||||
use libc::pread64;
|
||||
|
||||
unsafe {
|
||||
cvt(pread64(
|
||||
cvt(unsafe {
|
||||
pread64(
|
||||
self.as_raw_fd(),
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
cmp::min(buf.len(), READ_LIMIT),
|
||||
offset as off64_t,
|
||||
))
|
||||
.map(|n| n as usize)
|
||||
}
|
||||
offset as off64_t, // EINVAL if offset + count overflows
|
||||
)
|
||||
})
|
||||
.map(|n| n as usize)
|
||||
}
|
||||
|
||||
pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
|
||||
let ret = cvt(unsafe {
|
||||
libc::read(
|
||||
self.as_raw_fd(),
|
||||
cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
|
||||
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
|
||||
cmp::min(cursor.capacity(), READ_LIMIT),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Safety: `ret` bytes were written to the initialized portion of the buffer
|
||||
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
|
||||
unsafe {
|
||||
cursor.advance_unchecked(ret as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
|
||||
let ret = cvt(unsafe {
|
||||
pread64(
|
||||
self.as_raw_fd(),
|
||||
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
|
||||
cmp::min(cursor.capacity(), READ_LIMIT),
|
||||
offset as off64_t, // EINVAL if offset + count overflows
|
||||
)
|
||||
})?;
|
||||
|
||||
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
|
||||
unsafe {
|
||||
cursor.advance_unchecked(ret as usize);
|
||||
}
|
||||
|
|
@ -369,7 +389,6 @@ impl FileDesc {
|
|||
)))
|
||||
}
|
||||
|
||||
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
|
||||
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
#[cfg(not(any(
|
||||
all(target_os = "linux", not(target_env = "musl")),
|
||||
|
|
|
|||
|
|
@ -1463,6 +1463,10 @@ impl File {
|
|||
self.0.read_buf(cursor)
|
||||
}
|
||||
|
||||
pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
self.0.read_buf_at(cursor, offset)
|
||||
}
|
||||
|
||||
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
self.0.read_vectored_at(bufs, offset)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -605,6 +605,10 @@ impl File {
|
|||
self.handle.read_buf(cursor)
|
||||
}
|
||||
|
||||
pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
self.handle.read_buf_at(cursor, offset)
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.handle.write(buf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,19 @@ impl Handle {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
|
||||
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
|
||||
let read = unsafe {
|
||||
self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), Some(offset))
|
||||
}?;
|
||||
|
||||
// SAFETY: `read` bytes were written to the initialized portion of the buffer
|
||||
unsafe {
|
||||
cursor.advance_unchecked(read);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let mut me = self;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#![feature(clone_to_uninit, path_add_extension, maybe_uninit_slice, normalize_lexically)]
|
||||
#![feature(clone_to_uninit, maybe_uninit_slice, normalize_lexically)]
|
||||
|
||||
use std::clone::CloneToUninit;
|
||||
use std::ffi::OsStr;
|
||||
|
|
@ -2526,3 +2526,9 @@ fn normalize_lexically() {
|
|||
check_err(r"\\?\UNC\server\share\a\..\..");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// See issue#146183
|
||||
fn compare_path_to_str() {
|
||||
assert!(&PathBuf::from("x") == "x");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,14 +269,14 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.25"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||
checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -322,11 +322,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -364,12 +364,12 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||
|
||||
[[package]]
|
||||
name = "junction"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16"
|
||||
checksum = "c52f6e1bf39a7894f618c9d378904a11dbd7e10fe3ec20d1173600e79b1408d8"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -433,6 +433,15 @@ version = "2.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "normpath"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
|
|
@ -497,12 +506,13 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
|||
|
||||
[[package]]
|
||||
name = "opener"
|
||||
version = "0.5.2"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005"
|
||||
checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"winapi",
|
||||
"normpath",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -757,15 +767,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.19.0"
|
||||
version = "3.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600"
|
||||
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -929,11 +939,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1043,22 +1053,22 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1067,14 +1077,30 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1083,48 +1109,96 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ home = "0.5"
|
|||
ignore = "0.4"
|
||||
libc = "0.2"
|
||||
object = { version = "0.36.3", default-features = false, features = ["archive", "coff", "read_core", "std", "unaligned"] }
|
||||
opener = "0.5"
|
||||
opener = "0.8"
|
||||
semver = "1.0"
|
||||
serde = "1.0"
|
||||
# Directly use serde_derive rather than through the derive feature of serde to allow building both
|
||||
|
|
@ -67,7 +67,7 @@ tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter
|
|||
tempfile = { version = "3.15.0", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.junction]
|
||||
version = "1.0.0"
|
||||
version = "1.3.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.61"
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ impl Step for Rustc {
|
|||
|
||||
/// Represents a compiler that can check something.
|
||||
///
|
||||
/// If the compiler was created for `Mode::ToolRustc` or `Mode::Codegen`, it will also contain
|
||||
/// If the compiler was created for `Mode::ToolRustcPrivate` or `Mode::Codegen`, it will also contain
|
||||
/// .rmeta artifacts from rustc that was already checked using `build_compiler`.
|
||||
///
|
||||
/// All steps that use this struct in a "general way" (i.e. they don't know exactly what kind of
|
||||
|
|
@ -469,7 +469,7 @@ pub fn prepare_compiler_for_check(
|
|||
build_compiler
|
||||
}
|
||||
}
|
||||
Mode::ToolRustc | Mode::Codegen => {
|
||||
Mode::ToolRustcPrivate | Mode::Codegen => {
|
||||
// Check Rustc to produce the required rmeta artifacts for rustc_private, and then
|
||||
// return the build compiler that was used to check rustc.
|
||||
// We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass
|
||||
|
|
@ -767,19 +767,22 @@ fn run_tool_check_step(
|
|||
tool_check_step!(Rustdoc {
|
||||
path: "src/tools/rustdoc",
|
||||
alt_path: "src/librustdoc",
|
||||
mode: |_builder| Mode::ToolRustc
|
||||
mode: |_builder| Mode::ToolRustcPrivate
|
||||
});
|
||||
// Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
|
||||
// of a submodule. Since the SourceType only drives the deny-warnings
|
||||
// behavior, treat it as in-tree so that any new warnings in clippy will be
|
||||
// rejected.
|
||||
tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustc });
|
||||
tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustc });
|
||||
tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: |_builder| Mode::ToolRustc });
|
||||
tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustc });
|
||||
tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustcPrivate });
|
||||
tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustcPrivate });
|
||||
tool_check_step!(CargoMiri {
|
||||
path: "src/tools/miri/cargo-miri",
|
||||
mode: |_builder| Mode::ToolRustcPrivate
|
||||
});
|
||||
tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustcPrivate });
|
||||
tool_check_step!(RustAnalyzer {
|
||||
path: "src/tools/rust-analyzer",
|
||||
mode: |_builder| Mode::ToolRustc,
|
||||
mode: |_builder| Mode::ToolRustcPrivate,
|
||||
allow_features: tool::RustAnalyzer::ALLOW_FEATURES,
|
||||
enable_features: ["in-rust-tree"],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -366,8 +366,13 @@ impl Step for CodegenGcc {
|
|||
);
|
||||
self.build_compiler.configure_cargo(&mut cargo);
|
||||
|
||||
let _guard =
|
||||
builder.msg(Kind::Clippy, "rustc_codegen_gcc", Mode::ToolRustc, build_compiler, target);
|
||||
let _guard = builder.msg(
|
||||
Kind::Clippy,
|
||||
"rustc_codegen_gcc",
|
||||
Mode::ToolRustcPrivate,
|
||||
build_compiler,
|
||||
target,
|
||||
);
|
||||
|
||||
let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Codegen, target))
|
||||
.with_prefix("rustc_codegen_gcc-check");
|
||||
|
|
@ -478,8 +483,8 @@ lint_any!(
|
|||
Bootstrap, "src/bootstrap", "bootstrap", Mode::ToolTarget;
|
||||
BuildHelper, "src/build_helper", "build_helper", Mode::ToolTarget;
|
||||
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolTarget;
|
||||
CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustc;
|
||||
Clippy, "src/tools/clippy", "clippy", Mode::ToolRustc;
|
||||
CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustcPrivate;
|
||||
Clippy, "src/tools/clippy", "clippy", Mode::ToolRustcPrivate;
|
||||
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata", Mode::ToolTarget;
|
||||
Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTarget;
|
||||
CoverageDump, "src/tools/coverage-dump", "coverage-dump", Mode::ToolTarget;
|
||||
|
|
@ -487,14 +492,14 @@ lint_any!(
|
|||
Jsondoclint, "src/tools/jsondoclint", "jsondoclint", Mode::ToolTarget;
|
||||
LintDocs, "src/tools/lint-docs", "lint-docs", Mode::ToolTarget;
|
||||
LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", Mode::ToolTarget;
|
||||
Miri, "src/tools/miri", "miri", Mode::ToolRustc;
|
||||
Miri, "src/tools/miri", "miri", Mode::ToolRustcPrivate;
|
||||
MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools", Mode::ToolTarget;
|
||||
OptDist, "src/tools/opt-dist", "opt-dist", Mode::ToolTarget;
|
||||
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolTarget;
|
||||
RemoteTestServer, "src/tools/remote-test-server", "remote-test-server", Mode::ToolTarget;
|
||||
RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustc;
|
||||
Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustc;
|
||||
Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustc;
|
||||
RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustcPrivate;
|
||||
Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustcPrivate;
|
||||
Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustcPrivate;
|
||||
RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::ToolTarget;
|
||||
Tidy, "src/tools/tidy", "tidy", Mode::ToolTarget;
|
||||
TestFloatParse, "src/tools/test-float-parse", "test-float-parse", Mode::ToolStd;
|
||||
|
|
|
|||
|
|
@ -823,6 +823,18 @@ pub struct RustcDev {
|
|||
target: TargetSelection,
|
||||
}
|
||||
|
||||
impl RustcDev {
|
||||
pub fn new(builder: &Builder<'_>, target: TargetSelection) -> Self {
|
||||
Self {
|
||||
// We currently always ship a stage 2 rustc-dev component, so we build it with the
|
||||
// stage 1 compiler. This might change in the future.
|
||||
// The precise stage used here is important, so we hard-code it.
|
||||
build_compiler: builder.compiler(1, builder.config.host_target),
|
||||
target,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for RustcDev {
|
||||
type Output = Option<GeneratedTarball>;
|
||||
const DEFAULT: bool = true;
|
||||
|
|
@ -833,13 +845,7 @@ impl Step for RustcDev {
|
|||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
run.builder.ensure(RustcDev {
|
||||
// We currently always ship a stage 2 rustc-dev component, so we build it with the
|
||||
// stage 1 compiler. This might change in the future.
|
||||
// The precise stage used here is important, so we hard-code it.
|
||||
build_compiler: run.builder.compiler(1, run.builder.config.host_target),
|
||||
target: run.target,
|
||||
});
|
||||
run.builder.ensure(RustcDev::new(run.builder, run.target));
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
|
||||
|
|
|
|||
|
|
@ -995,7 +995,7 @@ macro_rules! tool_doc {
|
|||
// Build rustc docs so that we generate relative links.
|
||||
run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
|
||||
|
||||
(compilers.build_compiler(), Mode::ToolRustc)
|
||||
(compilers.build_compiler(), Mode::ToolRustcPrivate)
|
||||
} else {
|
||||
// bootstrap/host tools have to be documented with the stage 0 compiler
|
||||
(prepare_doc_compiler(run.builder, run.builder.host_target, 1), Mode::ToolBootstrap)
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ pub(crate) fn is_ci_llvm_available_for_target(
|
|||
// tier 1
|
||||
("aarch64-unknown-linux-gnu", false),
|
||||
("aarch64-apple-darwin", false),
|
||||
("aarch64-pc-windows-msvc", false),
|
||||
("i686-pc-windows-gnu", false),
|
||||
("i686-pc-windows-msvc", false),
|
||||
("i686-unknown-linux-gnu", false),
|
||||
|
|
@ -213,7 +214,6 @@ pub(crate) fn is_ci_llvm_available_for_target(
|
|||
("x86_64-pc-windows-gnu", true),
|
||||
("x86_64-pc-windows-msvc", true),
|
||||
// tier 2 with host tools
|
||||
("aarch64-pc-windows-msvc", false),
|
||||
("aarch64-unknown-linux-musl", false),
|
||||
("arm-unknown-linux-gnueabi", false),
|
||||
("arm-unknown-linux-gnueabihf", false),
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ impl Step for Miri {
|
|||
let mut miri = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
compilers.build_compiler(),
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
host,
|
||||
Kind::Run,
|
||||
"src/tools/miri",
|
||||
|
|
@ -487,7 +487,7 @@ impl Step for Rustfmt {
|
|||
let mut rustfmt = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
rustfmt_build.build_compiler,
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
host,
|
||||
Kind::Run,
|
||||
"src/tools/rustfmt",
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ impl Step for RustAnalyzer {
|
|||
let mut cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
self.compilers.build_compiler(),
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
host,
|
||||
Kind::Test,
|
||||
crate_path,
|
||||
|
|
@ -518,7 +518,7 @@ impl Step for Rustfmt {
|
|||
let mut cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
build_compiler,
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
target,
|
||||
Kind::Test,
|
||||
"src/tools/rustfmt",
|
||||
|
|
@ -571,7 +571,8 @@ impl Miri {
|
|||
cargo.env("MIRI_SYSROOT", &miri_sysroot);
|
||||
|
||||
let mut cargo = BootstrapCommand::from(cargo);
|
||||
let _guard = builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustc, compiler, target);
|
||||
let _guard =
|
||||
builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustcPrivate, compiler, target);
|
||||
cargo.run(builder);
|
||||
|
||||
// # Determine where Miri put its sysroot.
|
||||
|
|
@ -648,7 +649,7 @@ impl Step for Miri {
|
|||
let mut cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
miri.build_compiler,
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
host,
|
||||
Kind::Test,
|
||||
"src/tools/miri",
|
||||
|
|
@ -861,7 +862,7 @@ impl Step for Clippy {
|
|||
let mut cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
build_compiler,
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
target,
|
||||
Kind::Test,
|
||||
"src/tools/clippy",
|
||||
|
|
@ -872,7 +873,7 @@ impl Step for Clippy {
|
|||
cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler));
|
||||
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler));
|
||||
let host_libs =
|
||||
builder.stage_out(build_compiler, Mode::ToolRustc).join(builder.cargo_dir());
|
||||
builder.stage_out(build_compiler, Mode::ToolRustcPrivate).join(builder.cargo_dir());
|
||||
cargo.env("HOST_LIBS", host_libs);
|
||||
|
||||
// Build the standard library that the tests can use.
|
||||
|
|
@ -2411,7 +2412,7 @@ impl BookTest {
|
|||
let libs = if !self.dependencies.is_empty() {
|
||||
let mut lib_paths = vec![];
|
||||
for dep in self.dependencies {
|
||||
let mode = Mode::ToolRustc;
|
||||
let mode = Mode::ToolRustcPrivate;
|
||||
let target = builder.config.host_target;
|
||||
let cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
|
|
@ -2996,7 +2997,7 @@ impl Step for CrateRustdoc {
|
|||
let mut cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
compiler,
|
||||
Mode::ToolRustc,
|
||||
Mode::ToolRustcPrivate,
|
||||
target,
|
||||
builder.kind,
|
||||
"src/tools/rustdoc",
|
||||
|
|
@ -3182,6 +3183,7 @@ impl Step for Distcheck {
|
|||
/// check steps from those sources.
|
||||
/// - Check that selected dist components (`rust-src` only at the moment) at least have expected
|
||||
/// directory shape and crate manifests that cargo can generate a lockfile from.
|
||||
/// - Check that we can run `cargo metadata` on the workspace in the `rustc-dev` component
|
||||
///
|
||||
/// FIXME(#136822): dist components are under-tested.
|
||||
fn run(self, builder: &Builder<'_>) {
|
||||
|
|
@ -3189,63 +3191,93 @@ impl Step for Distcheck {
|
|||
// local source code, built artifacts or configuration by accident
|
||||
let root_dir = std::env::temp_dir().join("distcheck");
|
||||
|
||||
// Check that we can build some basic things from the plain source tarball
|
||||
builder.info("Distcheck plain source tarball");
|
||||
let plain_src_tarball = builder.ensure(dist::PlainSourceTarball);
|
||||
let plain_src_dir = root_dir.join("distcheck-plain-src");
|
||||
builder.clear_dir(&plain_src_dir);
|
||||
|
||||
let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS")
|
||||
.map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>())
|
||||
.unwrap_or_default();
|
||||
|
||||
command("tar")
|
||||
.arg("-xf")
|
||||
.arg(plain_src_tarball.tarball())
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(&plain_src_dir)
|
||||
.run(builder);
|
||||
command("./configure")
|
||||
.arg("--set")
|
||||
.arg("rust.omit-git-hash=false")
|
||||
.args(&configure_args)
|
||||
.arg("--enable-vendor")
|
||||
.current_dir(&plain_src_dir)
|
||||
.run(builder);
|
||||
command(helpers::make(&builder.config.host_target.triple))
|
||||
.arg("check")
|
||||
// Do not run the build as if we were in CI, otherwise git would be assumed to be
|
||||
// present, but we build from a tarball here
|
||||
.env("GITHUB_ACTIONS", "0")
|
||||
.current_dir(&plain_src_dir)
|
||||
.run(builder);
|
||||
|
||||
// Now make sure that rust-src has all of libstd's dependencies
|
||||
builder.info("Distcheck rust-src");
|
||||
let src_tarball = builder.ensure(dist::Src);
|
||||
let src_dir = root_dir.join("distcheck-src");
|
||||
builder.clear_dir(&src_dir);
|
||||
|
||||
command("tar")
|
||||
.arg("-xf")
|
||||
.arg(src_tarball.tarball())
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(&src_dir)
|
||||
.run(builder);
|
||||
|
||||
let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
|
||||
command(&builder.initial_cargo)
|
||||
// Will read the libstd Cargo.toml
|
||||
// which uses the unstable `public-dependency` feature.
|
||||
.env("RUSTC_BOOTSTRAP", "1")
|
||||
.arg("generate-lockfile")
|
||||
.arg("--manifest-path")
|
||||
.arg(&toml)
|
||||
.current_dir(&src_dir)
|
||||
.run(builder);
|
||||
distcheck_plain_source_tarball(builder, &root_dir.join("distcheck-rustc-src"));
|
||||
distcheck_rust_src(builder, &root_dir.join("distcheck-rust-src"));
|
||||
distcheck_rustc_dev(builder, &root_dir.join("distcheck-rustc-dev"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that we can build some basic things from the plain source tarball
|
||||
fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) {
|
||||
builder.info("Distcheck plain source tarball");
|
||||
let plain_src_tarball = builder.ensure(dist::PlainSourceTarball);
|
||||
builder.clear_dir(plain_src_dir);
|
||||
|
||||
let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS")
|
||||
.map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>())
|
||||
.unwrap_or_default();
|
||||
|
||||
command("tar")
|
||||
.arg("-xf")
|
||||
.arg(plain_src_tarball.tarball())
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(plain_src_dir)
|
||||
.run(builder);
|
||||
command("./configure")
|
||||
.arg("--set")
|
||||
.arg("rust.omit-git-hash=false")
|
||||
.args(&configure_args)
|
||||
.arg("--enable-vendor")
|
||||
.current_dir(plain_src_dir)
|
||||
.run(builder);
|
||||
command(helpers::make(&builder.config.host_target.triple))
|
||||
.arg("check")
|
||||
// Do not run the build as if we were in CI, otherwise git would be assumed to be
|
||||
// present, but we build from a tarball here
|
||||
.env("GITHUB_ACTIONS", "0")
|
||||
.current_dir(plain_src_dir)
|
||||
.run(builder);
|
||||
}
|
||||
|
||||
/// Check that rust-src has all of libstd's dependencies
|
||||
fn distcheck_rust_src(builder: &Builder<'_>, src_dir: &Path) {
|
||||
builder.info("Distcheck rust-src");
|
||||
let src_tarball = builder.ensure(dist::Src);
|
||||
builder.clear_dir(src_dir);
|
||||
|
||||
command("tar")
|
||||
.arg("-xf")
|
||||
.arg(src_tarball.tarball())
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(src_dir)
|
||||
.run(builder);
|
||||
|
||||
let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
|
||||
command(&builder.initial_cargo)
|
||||
// Will read the libstd Cargo.toml
|
||||
// which uses the unstable `public-dependency` feature.
|
||||
.env("RUSTC_BOOTSTRAP", "1")
|
||||
.arg("generate-lockfile")
|
||||
.arg("--manifest-path")
|
||||
.arg(&toml)
|
||||
.current_dir(src_dir)
|
||||
.run(builder);
|
||||
}
|
||||
|
||||
/// Check that rustc-dev's compiler crate source code can be loaded with `cargo metadata`
|
||||
fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) {
|
||||
builder.info("Distcheck rustc-dev");
|
||||
let tarball = builder.ensure(dist::RustcDev::new(builder, builder.host_target)).unwrap();
|
||||
builder.clear_dir(dir);
|
||||
|
||||
command("tar")
|
||||
.arg("-xf")
|
||||
.arg(tarball.tarball())
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(dir)
|
||||
.run(builder);
|
||||
|
||||
command(&builder.initial_cargo)
|
||||
.arg("metadata")
|
||||
.arg("--manifest-path")
|
||||
.arg("rustc-dev/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml")
|
||||
.env("RUSTC_BOOTSTRAP", "1")
|
||||
// We might not have a globally available `rustc` binary on CI
|
||||
.env("RUSTC", &builder.initial_rustc)
|
||||
.current_dir(dir)
|
||||
.run(builder);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Bootstrap;
|
||||
|
||||
|
|
@ -3294,9 +3326,7 @@ impl Step for Bootstrap {
|
|||
.env("INSTA_WORKSPACE_ROOT", &builder.src)
|
||||
.env("RUSTC_BOOTSTRAP", "1");
|
||||
|
||||
// bootstrap tests are racy on directory creation so just run them one at a time.
|
||||
// Since there's not many this shouldn't be a problem.
|
||||
run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder);
|
||||
run_cargo_test(cargo, &[], &[], None, host, builder);
|
||||
}
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl Step for ToolBuild {
|
|||
let path = self.path;
|
||||
|
||||
match self.mode {
|
||||
Mode::ToolRustc => {
|
||||
Mode::ToolRustcPrivate => {
|
||||
// FIXME: remove this, it's only needed for download-rustc...
|
||||
if !self.build_compiler.is_forced_compiler() && builder.download_rustc() {
|
||||
builder.std(self.build_compiler, self.build_compiler.host);
|
||||
|
|
@ -123,7 +123,7 @@ impl Step for ToolBuild {
|
|||
|
||||
// Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer)
|
||||
// could use the additional optimizations.
|
||||
if self.mode == Mode::ToolRustc && is_lto_stage(&self.build_compiler) {
|
||||
if self.mode == Mode::ToolRustcPrivate && is_lto_stage(&self.build_compiler) {
|
||||
let lto = match builder.config.rust_lto {
|
||||
RustcLto::Off => Some("off"),
|
||||
RustcLto::Thin => Some("thin"),
|
||||
|
|
@ -607,7 +607,7 @@ impl Step for ErrorIndex {
|
|||
build_compiler: self.compilers.build_compiler,
|
||||
target: self.compilers.target(),
|
||||
tool: "error_index_generator",
|
||||
mode: Mode::ToolRustc,
|
||||
mode: Mode::ToolRustcPrivate,
|
||||
path: "src/tools/error_index_generator",
|
||||
source_type: SourceType::InTree,
|
||||
extra_features: Vec::new(),
|
||||
|
|
@ -671,7 +671,7 @@ impl Step for RemoteTestServer {
|
|||
/// Represents `Rustdoc` that either comes from the external stage0 sysroot or that is built
|
||||
/// locally.
|
||||
/// Rustdoc is special, because it both essentially corresponds to a `Compiler` (that can be
|
||||
/// externally provided), but also to a `ToolRustc` tool.
|
||||
/// externally provided), but also to a `ToolRustcPrivate` tool.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rustdoc {
|
||||
/// If the stage of `target_compiler` is `0`, then rustdoc is externally provided.
|
||||
|
|
@ -759,7 +759,7 @@ impl Step for Rustdoc {
|
|||
// the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
|
||||
// rustdoc a different name.
|
||||
tool: "rustdoc_tool_binary",
|
||||
mode: Mode::ToolRustc,
|
||||
mode: Mode::ToolRustcPrivate,
|
||||
path: "src/tools/rustdoc",
|
||||
source_type: SourceType::InTree,
|
||||
extra_features,
|
||||
|
|
@ -1048,7 +1048,7 @@ impl Step for RustAnalyzer {
|
|||
build_compiler,
|
||||
target,
|
||||
tool: "rust-analyzer",
|
||||
mode: Mode::ToolRustc,
|
||||
mode: Mode::ToolRustcPrivate,
|
||||
path: "src/tools/rust-analyzer",
|
||||
extra_features: vec!["in-rust-tree".to_owned()],
|
||||
source_type: SourceType::InTree,
|
||||
|
|
@ -1105,7 +1105,7 @@ impl Step for RustAnalyzerProcMacroSrv {
|
|||
build_compiler: self.compilers.build_compiler,
|
||||
target: self.compilers.target(),
|
||||
tool: "rust-analyzer-proc-macro-srv",
|
||||
mode: Mode::ToolRustc,
|
||||
mode: Mode::ToolRustcPrivate,
|
||||
path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli",
|
||||
extra_features: vec!["in-rust-tree".to_owned()],
|
||||
source_type: SourceType::InTree,
|
||||
|
|
@ -1352,7 +1352,7 @@ impl RustcPrivateCompilers {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a step that builds an extended `Mode::ToolRustc` tool
|
||||
/// Creates a step that builds an extended `Mode::ToolRustcPrivate` tool
|
||||
/// and installs it into the sysroot of a corresponding compiler.
|
||||
macro_rules! tool_rustc_extended {
|
||||
(
|
||||
|
|
@ -1466,7 +1466,7 @@ fn build_extended_rustc_tool(
|
|||
build_compiler,
|
||||
target,
|
||||
tool: tool_name,
|
||||
mode: Mode::ToolRustc,
|
||||
mode: Mode::ToolRustcPrivate,
|
||||
path,
|
||||
extra_features,
|
||||
source_type: SourceType::InTree,
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ impl Builder<'_> {
|
|||
if cmd_kind == Kind::Doc {
|
||||
let my_out = match mode {
|
||||
// This is the intended out directory for compiler documentation.
|
||||
Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap => {
|
||||
Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap => {
|
||||
self.compiler_doc_out(target)
|
||||
}
|
||||
Mode::Std => {
|
||||
|
|
@ -583,7 +583,7 @@ impl Builder<'_> {
|
|||
|
||||
// We synthetically interpret a stage0 compiler used to build tools as a
|
||||
// "raw" compiler in that it's the exact snapshot we download. For things like
|
||||
// ToolRustc, we would have to use the artificial stage0-sysroot compiler instead.
|
||||
// ToolRustcPrivate, we would have to use the artificial stage0-sysroot compiler instead.
|
||||
let use_snapshot =
|
||||
mode == Mode::ToolBootstrap || (mode == Mode::ToolTarget && build_compiler_stage == 0);
|
||||
assert!(!use_snapshot || build_compiler_stage == 0 || self.local_rebuild);
|
||||
|
|
@ -643,7 +643,8 @@ impl Builder<'_> {
|
|||
// sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
|
||||
// library unnecessary. This can be removed when windows-rs enables raw-dylib
|
||||
// unconditionally.
|
||||
if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap | Mode::ToolTarget = mode {
|
||||
if let Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget = mode
|
||||
{
|
||||
rustflags.arg("--cfg=windows_raw_dylib");
|
||||
}
|
||||
|
||||
|
|
@ -657,7 +658,7 @@ impl Builder<'_> {
|
|||
// - rust-analyzer, due to the rowan crate
|
||||
// so we exclude an entire category of steps here due to lack of fine-grained control over
|
||||
// rustflags.
|
||||
if self.config.rust_randomize_layout && mode != Mode::ToolRustc {
|
||||
if self.config.rust_randomize_layout && mode != Mode::ToolRustcPrivate {
|
||||
rustflags.arg("-Zrandomize-layout");
|
||||
}
|
||||
|
||||
|
|
@ -717,7 +718,7 @@ impl Builder<'_> {
|
|||
|
||||
match mode {
|
||||
Mode::Std | Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {}
|
||||
Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
|
||||
Mode::Rustc | Mode::Codegen | Mode::ToolRustcPrivate => {
|
||||
// Build proc macros both for the host and the target unless proc-macros are not
|
||||
// supported by the target.
|
||||
if target != compiler.host && cmd_kind != Kind::Check {
|
||||
|
|
@ -778,7 +779,7 @@ impl Builder<'_> {
|
|||
"binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
|
||||
.to_string()
|
||||
}
|
||||
Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
|
||||
Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustcPrivate => String::new(),
|
||||
};
|
||||
|
||||
cargo.arg("-j").arg(self.jobs().to_string());
|
||||
|
|
@ -825,7 +826,7 @@ impl Builder<'_> {
|
|||
// rustc step and one that we just built. This isn't always a
|
||||
// problem, somehow -- not really clear why -- but we know that this
|
||||
// fixes things.
|
||||
Mode::ToolRustc => metadata.push_str("tool-rustc"),
|
||||
Mode::ToolRustcPrivate => metadata.push_str("tool-rustc"),
|
||||
// Same for codegen backends.
|
||||
Mode::Codegen => metadata.push_str("codegen"),
|
||||
_ => {}
|
||||
|
|
@ -877,8 +878,11 @@ impl Builder<'_> {
|
|||
.env("RUSTC_LIBDIR", libdir)
|
||||
.env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
|
||||
.env("RUSTDOC_REAL", rustdoc_path)
|
||||
.env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
|
||||
.env("RUSTC_BREAK_ON_ICE", "1");
|
||||
.env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir());
|
||||
|
||||
if self.config.rust_break_on_ice {
|
||||
cargo.env("RUSTC_BREAK_ON_ICE", "1");
|
||||
}
|
||||
|
||||
// Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
|
||||
// sysroot depending on whether we're building build scripts.
|
||||
|
|
@ -917,7 +921,7 @@ impl Builder<'_> {
|
|||
let debuginfo_level = match mode {
|
||||
Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
|
||||
Mode::Std => self.config.rust_debuginfo_level_std,
|
||||
Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
|
||||
Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustcPrivate | Mode::ToolTarget => {
|
||||
self.config.rust_debuginfo_level_tools
|
||||
}
|
||||
};
|
||||
|
|
@ -930,7 +934,7 @@ impl Builder<'_> {
|
|||
match mode {
|
||||
Mode::Std => self.config.std_debug_assertions,
|
||||
Mode::Rustc | Mode::Codegen => self.config.rustc_debug_assertions,
|
||||
Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
|
||||
Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustcPrivate | Mode::ToolTarget => {
|
||||
self.config.tools_debug_assertions
|
||||
}
|
||||
}
|
||||
|
|
@ -1005,7 +1009,7 @@ impl Builder<'_> {
|
|||
}
|
||||
Mode::Std
|
||||
| Mode::ToolBootstrap
|
||||
| Mode::ToolRustc
|
||||
| Mode::ToolRustcPrivate
|
||||
| Mode::ToolStd
|
||||
| Mode::ToolTarget => {
|
||||
if let Some(ref map_to) =
|
||||
|
|
@ -1078,7 +1082,7 @@ impl Builder<'_> {
|
|||
// requirement, but the `-L` library path is not propagated across
|
||||
// separate Cargo projects. We can add LLVM's library path to the
|
||||
// rustc args as a workaround.
|
||||
if (mode == Mode::ToolRustc || mode == Mode::Codegen)
|
||||
if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen)
|
||||
&& let Some(llvm_config) = self.llvm_config(target)
|
||||
{
|
||||
let llvm_libdir =
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use crate::core::build_steps::doc::DocumentationFormat;
|
|||
use crate::core::config::Config;
|
||||
use crate::utils::cache::ExecutedStep;
|
||||
use crate::utils::helpers::get_host_target;
|
||||
use crate::utils::tests::ConfigBuilder;
|
||||
use crate::utils::tests::git::{GitCtx, git_test};
|
||||
use crate::utils::tests::{ConfigBuilder, TestCtx};
|
||||
|
||||
static TEST_TRIPLE_1: &str = "i686-unknown-haiku";
|
||||
static TEST_TRIPLE_2: &str = "i686-unknown-hurd-gnu";
|
||||
|
|
@ -22,38 +22,13 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
|
|||
}
|
||||
|
||||
fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config {
|
||||
let cmd = cmd.iter().copied().map(String::from).collect::<Vec<_>>();
|
||||
let mut config = Config::parse(Flags::parse(&cmd));
|
||||
// don't save toolstates
|
||||
config.save_toolstates = None;
|
||||
config.set_dry_run(DryRun::SelfCheck);
|
||||
|
||||
// Ignore most submodules, since we don't need them for a dry run, and the
|
||||
// tests run much faster without them.
|
||||
//
|
||||
// The src/doc/book submodule is needed because TheBook step tries to
|
||||
// access files even during a dry-run (may want to consider just skipping
|
||||
// that in a dry run).
|
||||
let submodule_build = Build::new(Config {
|
||||
// don't include LLVM, so CI doesn't require ninja/cmake to be installed
|
||||
rust_codegen_backends: vec![],
|
||||
..Config::parse(Flags::parse(&["check".to_owned()]))
|
||||
});
|
||||
submodule_build.require_submodule("src/doc/book", None);
|
||||
config.submodules = Some(false);
|
||||
|
||||
config.ninja_in_file = false;
|
||||
// try to avoid spurious failures in dist where we create/delete each others file
|
||||
// HACK: rather than pull in `tempdir`, use the one that cargo has conveniently created for us
|
||||
let dir = Path::new(env!("OUT_DIR"))
|
||||
.join("tmp-rustbuild-tests")
|
||||
.join(&thread::current().name().unwrap_or("unknown").replace(":", "-"));
|
||||
t!(fs::create_dir_all(&dir));
|
||||
config.out = dir;
|
||||
config.host_target = TargetSelection::from_user(TEST_TRIPLE_1);
|
||||
config.hosts = host.iter().map(|s| TargetSelection::from_user(s)).collect();
|
||||
config.targets = target.iter().map(|s| TargetSelection::from_user(s)).collect();
|
||||
config
|
||||
TestCtx::new()
|
||||
.config(cmd[0])
|
||||
.args(&cmd[1..])
|
||||
.hosts(host)
|
||||
.targets(target)
|
||||
.args(&["--build", TEST_TRIPLE_1])
|
||||
.create_config()
|
||||
}
|
||||
|
||||
fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> {
|
||||
|
|
@ -547,8 +522,8 @@ mod snapshot {
|
|||
|
||||
use crate::core::build_steps::{compile, dist, doc, test, tool};
|
||||
use crate::core::builder::tests::{
|
||||
RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, configure_with_args,
|
||||
first, host_target, render_steps, run_build,
|
||||
RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, first, host_target,
|
||||
render_steps, run_build,
|
||||
};
|
||||
use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata};
|
||||
use crate::core::config::TargetSelection;
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ pub struct Config {
|
|||
pub rust_lto: RustcLto,
|
||||
pub rust_validate_mir_opts: Option<u32>,
|
||||
pub rust_std_features: BTreeSet<String>,
|
||||
pub rust_break_on_ice: bool,
|
||||
pub llvm_profile_use: Option<String>,
|
||||
pub llvm_profile_generate: bool,
|
||||
pub llvm_libunwind_default: Option<LlvmLibunwind>,
|
||||
|
|
@ -550,6 +551,7 @@ impl Config {
|
|||
strip: rust_strip,
|
||||
lld_mode: rust_lld_mode,
|
||||
std_features: rust_std_features,
|
||||
break_on_ice: rust_break_on_ice,
|
||||
} = toml.rust.unwrap_or_default();
|
||||
|
||||
let Llvm {
|
||||
|
|
@ -1269,6 +1271,7 @@ impl Config {
|
|||
reproducible_artifacts: flags_reproducible_artifact,
|
||||
reuse: build_reuse.map(PathBuf::from),
|
||||
rust_analyzer_info,
|
||||
rust_break_on_ice: rust_break_on_ice.unwrap_or(true),
|
||||
rust_codegen_backends: rust_codegen_backends
|
||||
.map(|backends| parse_codegen_backends(backends, "rust"))
|
||||
.unwrap_or(vec![CodegenBackendKind::Llvm]),
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ define_config! {
|
|||
lto: Option<String> = "lto",
|
||||
validate_mir_opts: Option<u32> = "validate-mir-opts",
|
||||
std_features: Option<BTreeSet<String>> = "std-features",
|
||||
break_on_ice: Option<bool> = "break-on-ice",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,6 +356,7 @@ pub fn check_incompatible_options_for_ci_rustc(
|
|||
download_rustc: _,
|
||||
validate_mir_opts: _,
|
||||
frame_pointers: _,
|
||||
break_on_ice: _,
|
||||
} = ci_rust_config;
|
||||
|
||||
// There are two kinds of checks for CI rustc incompatible options:
|
||||
|
|
|
|||
|
|
@ -85,12 +85,12 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
|
|||
const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
|
||||
(Some(Mode::Rustc), "bootstrap", None),
|
||||
(Some(Mode::Codegen), "bootstrap", None),
|
||||
(Some(Mode::ToolRustc), "bootstrap", None),
|
||||
(Some(Mode::ToolRustcPrivate), "bootstrap", None),
|
||||
(Some(Mode::ToolStd), "bootstrap", None),
|
||||
(Some(Mode::Rustc), "llvm_enzyme", None),
|
||||
(Some(Mode::Codegen), "llvm_enzyme", None),
|
||||
(Some(Mode::ToolRustc), "llvm_enzyme", None),
|
||||
(Some(Mode::ToolRustc), "rust_analyzer", None),
|
||||
(Some(Mode::ToolRustcPrivate), "llvm_enzyme", None),
|
||||
(Some(Mode::ToolRustcPrivate), "rust_analyzer", None),
|
||||
(Some(Mode::ToolStd), "rust_analyzer", None),
|
||||
// Any library specific cfgs like `target_os`, `target_arch` should be put in
|
||||
// priority the `[lints.rust.unexpected_cfgs.check-cfg]` table
|
||||
|
|
@ -334,17 +334,18 @@ pub enum Mode {
|
|||
/// compiletest which needs libtest.
|
||||
ToolStd,
|
||||
|
||||
/// Build a tool which uses the locally built rustc and the target std,
|
||||
/// Build a tool which uses the `rustc_private` mechanism, and thus
|
||||
/// the locally built rustc rlib artifacts,
|
||||
/// placing the output in the "stageN-tools" directory. This is used for
|
||||
/// anything that needs a fully functional rustc, such as rustdoc, clippy,
|
||||
/// cargo, rustfmt, miri, etc.
|
||||
ToolRustc,
|
||||
/// everything that links to rustc as a library, such as rustdoc, clippy,
|
||||
/// rustfmt, miri, etc.
|
||||
ToolRustcPrivate,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn is_tool(&self) -> bool {
|
||||
match self {
|
||||
Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd | Mode::ToolTarget => true,
|
||||
Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => true,
|
||||
Mode::Std | Mode::Codegen | Mode::Rustc => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -353,7 +354,7 @@ impl Mode {
|
|||
match self {
|
||||
Mode::Std | Mode::Codegen => true,
|
||||
Mode::ToolBootstrap
|
||||
| Mode::ToolRustc
|
||||
| Mode::ToolRustcPrivate
|
||||
| Mode::ToolStd
|
||||
| Mode::ToolTarget
|
||||
| Mode::Rustc => false,
|
||||
|
|
@ -924,7 +925,7 @@ impl Build {
|
|||
Mode::Rustc => (Some(build_compiler.stage + 1), "rustc"),
|
||||
Mode::Codegen => (Some(build_compiler.stage + 1), "codegen"),
|
||||
Mode::ToolBootstrap => bootstrap_tool(),
|
||||
Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage + 1), "tools"),
|
||||
Mode::ToolStd | Mode::ToolRustcPrivate => (Some(build_compiler.stage + 1), "tools"),
|
||||
Mode::ToolTarget => {
|
||||
// If we're not cross-compiling (the common case), share the target directory with
|
||||
// bootstrap tools to reuse the build cache.
|
||||
|
|
@ -1145,7 +1146,7 @@ impl Build {
|
|||
| Mode::ToolBootstrap
|
||||
| Mode::ToolTarget
|
||||
| Mode::ToolStd
|
||||
| Mode::ToolRustc,
|
||||
| Mode::ToolRustcPrivate,
|
||||
)
|
||||
| None => target_and_stage.stage + 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use std::{env, iter};
|
|||
|
||||
use super::*;
|
||||
use crate::core::config::{Target, TargetSelection};
|
||||
use crate::{Build, Config, Flags};
|
||||
use crate::utils::tests::TestCtx;
|
||||
use crate::{Build, Config, Flags, t};
|
||||
|
||||
#[test]
|
||||
fn test_ndk_compiler_c() {
|
||||
|
|
@ -68,7 +69,8 @@ fn test_language_clang() {
|
|||
|
||||
#[test]
|
||||
fn test_new_cc_build() {
|
||||
let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
|
||||
let config = TestCtx::new().config("build").create_config();
|
||||
let build = Build::new(config);
|
||||
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
|
||||
let cfg = new_cc_build(&build, target.clone());
|
||||
let compiler = cfg.get_compiler();
|
||||
|
|
@ -77,7 +79,8 @@ fn test_new_cc_build() {
|
|||
|
||||
#[test]
|
||||
fn test_default_compiler_wasi() {
|
||||
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
|
||||
let config = TestCtx::new().config("build").create_config();
|
||||
let mut build = Build::new(config);
|
||||
let target = TargetSelection::from_user("wasm32-wasi");
|
||||
let wasi_sdk = PathBuf::from("/wasi-sdk");
|
||||
build.wasi_sdk_path = Some(wasi_sdk.clone());
|
||||
|
|
@ -98,7 +101,8 @@ fn test_default_compiler_wasi() {
|
|||
|
||||
#[test]
|
||||
fn test_default_compiler_fallback() {
|
||||
let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
|
||||
let config = TestCtx::new().config("build").create_config();
|
||||
let build = Build::new(config);
|
||||
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
|
||||
let mut cfg = cc::Build::new();
|
||||
let result = default_compiler(&mut cfg, Language::C, target, &build);
|
||||
|
|
@ -107,7 +111,8 @@ fn test_default_compiler_fallback() {
|
|||
|
||||
#[test]
|
||||
fn test_find_target_with_config() {
|
||||
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
|
||||
let config = TestCtx::new().config("build").create_config();
|
||||
let mut build = Build::new(config);
|
||||
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
|
||||
let mut target_config = Target::default();
|
||||
target_config.cc = Some(PathBuf::from("dummy-cc"));
|
||||
|
|
@ -128,7 +133,8 @@ fn test_find_target_with_config() {
|
|||
|
||||
#[test]
|
||||
fn test_find_target_without_config() {
|
||||
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
|
||||
let config = TestCtx::new().config("build").create_config();
|
||||
let mut build = Build::new(config);
|
||||
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
|
||||
build.config.target_config.clear();
|
||||
fill_target_compiler(&mut build, target.clone());
|
||||
|
|
@ -141,7 +147,8 @@ fn test_find_target_without_config() {
|
|||
|
||||
#[test]
|
||||
fn test_find() {
|
||||
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
|
||||
let config = TestCtx::new().config("build").create_config();
|
||||
let mut build = Build::new(config);
|
||||
let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu");
|
||||
let target2 = TargetSelection::from_user("x86_64-unknown-openbsd");
|
||||
build.targets.push(target1.clone());
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue