mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-30 00:23:41 +00:00
Auto merge of #100611 - matthiaskrgr:rollup-rxj10ur, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #100338 (when there are 3 or more return statements in the loop) - #100384 (Add support for generating unique profraw files by default when using `-C instrument-coverage`) - #100460 (Update the minimum external LLVM to 13) - #100567 (Add missing closing quote) - #100590 (Suggest adding an array length if possible) - #100600 (Rename Machine memory hooks to suggest when they run) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
8556e6620e
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
- name: mingw-check
|
||||
os: ubuntu-20.04-xl
|
||||
env: {}
|
||||
- name: x86_64-gnu-llvm-12
|
||||
- name: x86_64-gnu-llvm-13
|
||||
os: ubuntu-20.04-xl
|
||||
env: {}
|
||||
- name: x86_64-gnu-tools
|
||||
@ -274,11 +274,11 @@ jobs:
|
||||
- name: x86_64-gnu-distcheck
|
||||
os: ubuntu-20.04-xl
|
||||
env: {}
|
||||
- name: x86_64-gnu-llvm-12
|
||||
- name: x86_64-gnu-llvm-13
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
os: ubuntu-20.04-xl
|
||||
- name: x86_64-gnu-llvm-12-stage1
|
||||
- name: x86_64-gnu-llvm-13-stage1
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
os: ubuntu-20.04-xl
|
||||
|
@ -48,7 +48,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{struct_span_err, Applicability, Handler};
|
||||
use rustc_errors::{struct_span_err, Applicability, Handler, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
||||
@ -2233,7 +2233,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
c.value.span,
|
||||
"using `_` for array lengths is unstable",
|
||||
)
|
||||
.emit();
|
||||
.stash(c.value.span, StashKey::UnderscoreForArrayLengths);
|
||||
hir::ArrayLen::Body(self.lower_anon_const(c))
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ use crate::builder::Builder;
|
||||
use crate::common::Funclet;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::llvm;
|
||||
use crate::llvm_util;
|
||||
use crate::type_::Type;
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
use crate::value::Value;
|
||||
@ -419,13 +418,6 @@ pub(crate) fn inline_asm_call<'ll>(
|
||||
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr().cast(), cons.len());
|
||||
debug!("constraint verification result: {:?}", constraints_ok);
|
||||
if constraints_ok {
|
||||
if unwind && llvm_util::get_version() < (13, 0, 0) {
|
||||
bx.cx.sess().span_fatal(
|
||||
line_spans[0],
|
||||
"unwinding from inline assembly is only supported on llvm >= 13.",
|
||||
);
|
||||
}
|
||||
|
||||
let v = llvm::LLVMRustInlineAsm(
|
||||
fty,
|
||||
asm.as_ptr().cast(),
|
||||
|
@ -5,7 +5,7 @@ use crate::back::profiling::{
|
||||
use crate::base;
|
||||
use crate::common;
|
||||
use crate::consts;
|
||||
use crate::llvm::{self, DiagnosticInfo, PassManager, SMDiagnostic};
|
||||
use crate::llvm::{self, DiagnosticInfo, PassManager};
|
||||
use crate::llvm_util;
|
||||
use crate::type_::Type;
|
||||
use crate::LlvmCodegenBackend;
|
||||
@ -304,7 +304,6 @@ impl<'a> DiagnosticHandlers<'a> {
|
||||
remark_passes.as_ptr(),
|
||||
remark_passes.len(),
|
||||
);
|
||||
llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data.cast());
|
||||
DiagnosticHandlers { data, llcx, old_handler }
|
||||
}
|
||||
}
|
||||
@ -312,9 +311,7 @@ impl<'a> DiagnosticHandlers<'a> {
|
||||
|
||||
impl<'a> Drop for DiagnosticHandlers<'a> {
|
||||
fn drop(&mut self) {
|
||||
use std::ptr::null_mut;
|
||||
unsafe {
|
||||
llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, null_mut());
|
||||
llvm::LLVMRustContextSetDiagnosticHandler(self.llcx, self.old_handler);
|
||||
drop(Box::from_raw(self.data));
|
||||
}
|
||||
@ -342,16 +339,6 @@ fn report_inline_asm(
|
||||
cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void, cookie: c_uint) {
|
||||
if user.is_null() {
|
||||
return;
|
||||
}
|
||||
let (cgcx, _) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler));
|
||||
|
||||
let smdiag = llvm::diagnostic::SrcMgrDiagnostic::unpack(diag);
|
||||
report_inline_asm(cgcx, smdiag.message, smdiag.level, cookie, smdiag.source);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) {
|
||||
if user.is_null() {
|
||||
return;
|
||||
@ -423,6 +410,14 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> {
|
||||
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
|
||||
}
|
||||
|
||||
fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
|
||||
if config.instrument_coverage {
|
||||
Some(CString::new("default_%m_%p.profraw").unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
diag_handler: &Handler,
|
||||
@ -438,6 +433,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
let pgo_use_path = get_pgo_use_path(config);
|
||||
let pgo_sample_use_path = get_pgo_sample_use_path(config);
|
||||
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
|
||||
let instr_profile_output_path = get_instr_profile_output_path(config);
|
||||
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
|
||||
let sanitizer_options = if !is_lto {
|
||||
Some(llvm::SanitizerOptions {
|
||||
@ -488,6 +484,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
config.instrument_coverage,
|
||||
instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
config.instrument_gcov,
|
||||
pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
config.debug_info_for_profiling,
|
||||
|
@ -3,7 +3,6 @@ use crate::common::Funclet;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::llvm::{self, BasicBlock, False};
|
||||
use crate::llvm::{AtomicOrdering, AtomicRmwBinOp, SynchronizationScope};
|
||||
use crate::llvm_util;
|
||||
use crate::type_::Type;
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
use crate::value::Value;
|
||||
@ -1038,25 +1037,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
dst: &'ll Value,
|
||||
cmp: &'ll Value,
|
||||
src: &'ll Value,
|
||||
mut order: rustc_codegen_ssa::common::AtomicOrdering,
|
||||
order: rustc_codegen_ssa::common::AtomicOrdering,
|
||||
failure_order: rustc_codegen_ssa::common::AtomicOrdering,
|
||||
weak: bool,
|
||||
) -> &'ll Value {
|
||||
let weak = if weak { llvm::True } else { llvm::False };
|
||||
if llvm_util::get_version() < (13, 0, 0) {
|
||||
use rustc_codegen_ssa::common::AtomicOrdering::*;
|
||||
// Older llvm has the pre-C++17 restriction on
|
||||
// success and failure memory ordering,
|
||||
// requiring the former to be at least as strong as the latter.
|
||||
// So, for llvm 12, we upgrade the success ordering to a stronger
|
||||
// one if necessary.
|
||||
match (order, failure_order) {
|
||||
(Relaxed, Acquire) => order = Acquire,
|
||||
(Release, Acquire) => order = AcquireRelease,
|
||||
(_, SequentiallyConsistent) => order = SequentiallyConsistent,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
llvm::LLVMRustBuildAtomicCmpXchg(
|
||||
self.llbuilder,
|
||||
@ -1444,51 +1429,37 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn fptoint_sat_broken_in_llvm(&self) -> bool {
|
||||
match self.tcx.sess.target.arch.as_ref() {
|
||||
// FIXME - https://bugs.llvm.org/show_bug.cgi?id=50083
|
||||
"riscv64" => llvm_util::get_version() < (13, 0, 0),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn fptoint_sat(
|
||||
&mut self,
|
||||
signed: bool,
|
||||
val: &'ll Value,
|
||||
dest_ty: &'ll Type,
|
||||
) -> Option<&'ll Value> {
|
||||
if !self.fptoint_sat_broken_in_llvm() {
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector
|
||||
{
|
||||
assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
|
||||
(
|
||||
self.cx.element_type(src_ty),
|
||||
self.cx.element_type(dest_ty),
|
||||
Some(self.cx.vector_length(src_ty)),
|
||||
)
|
||||
} else {
|
||||
(src_ty, dest_ty, None)
|
||||
};
|
||||
let float_width = self.cx.float_width(float_ty);
|
||||
let int_width = self.cx.int_width(int_ty);
|
||||
|
||||
let instr = if signed { "fptosi" } else { "fptoui" };
|
||||
let name = if let Some(vector_length) = vector_length {
|
||||
format!(
|
||||
"llvm.{}.sat.v{}i{}.v{}f{}",
|
||||
instr, vector_length, int_width, vector_length, float_width
|
||||
)
|
||||
} else {
|
||||
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
|
||||
};
|
||||
let f =
|
||||
self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
|
||||
Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None))
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
|
||||
assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
|
||||
(
|
||||
self.cx.element_type(src_ty),
|
||||
self.cx.element_type(dest_ty),
|
||||
Some(self.cx.vector_length(src_ty)),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(src_ty, dest_ty, None)
|
||||
};
|
||||
let float_width = self.cx.float_width(float_ty);
|
||||
let int_width = self.cx.int_width(int_ty);
|
||||
|
||||
let instr = if signed { "fptosi" } else { "fptoui" };
|
||||
let name = if let Some(vector_length) = vector_length {
|
||||
format!(
|
||||
"llvm.{}.sat.v{}i{}.v{}f{}",
|
||||
instr, vector_length, int_width, vector_length, float_width
|
||||
)
|
||||
} else {
|
||||
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
|
||||
};
|
||||
let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
|
||||
Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None))
|
||||
}
|
||||
|
||||
pub(crate) fn landing_pad(
|
||||
|
@ -142,17 +142,6 @@ pub unsafe fn create_module<'ll>(
|
||||
|
||||
let mut target_data_layout = sess.target.data_layout.to_string();
|
||||
let llvm_version = llvm_util::get_version();
|
||||
if llvm_version < (13, 0, 0) {
|
||||
if sess.target.arch == "powerpc64" {
|
||||
target_data_layout = target_data_layout.replace("-S128", "");
|
||||
}
|
||||
if sess.target.arch == "wasm32" {
|
||||
target_data_layout = "e-m:e-p:32:32-i64:64-n32:64-S128".to_string();
|
||||
}
|
||||
if sess.target.arch == "wasm64" {
|
||||
target_data_layout = "e-m:e-p:64:64-i64:64-n32:64-S128".to_string();
|
||||
}
|
||||
}
|
||||
if llvm_version < (14, 0, 0) {
|
||||
if sess.target.llvm_target == "i686-pc-windows-msvc"
|
||||
|| sess.target.llvm_target == "i586-pc-windows-msvc"
|
||||
|
@ -2360,6 +2360,7 @@ extern "C" {
|
||||
PGOGenPath: *const c_char,
|
||||
PGOUsePath: *const c_char,
|
||||
InstrumentCoverage: bool,
|
||||
InstrProfileOutput: *const c_char,
|
||||
InstrumentGCOV: bool,
|
||||
PGOSampleUsePath: *const c_char,
|
||||
DebugInfoForProfiling: bool,
|
||||
@ -2423,12 +2424,6 @@ extern "C" {
|
||||
cookie_out: &mut c_uint,
|
||||
) -> &'a SMDiagnostic;
|
||||
|
||||
pub fn LLVMRustSetInlineAsmDiagnosticHandler(
|
||||
C: &Context,
|
||||
H: InlineAsmDiagHandlerTy,
|
||||
CX: *mut c_void,
|
||||
);
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn LLVMRustUnpackSMDiagnostic(
|
||||
d: &SMDiagnostic,
|
||||
|
@ -92,16 +92,6 @@ unsafe fn configure_llvm(sess: &Session) {
|
||||
add("-generate-arange-section", false);
|
||||
}
|
||||
|
||||
// Disable the machine outliner by default in LLVM versions 11 and LLVM
|
||||
// version 12, where it leads to miscompilation.
|
||||
//
|
||||
// Ref:
|
||||
// - https://github.com/rust-lang/rust/issues/85351
|
||||
// - https://reviews.llvm.org/D103167
|
||||
if llvm_util::get_version() < (13, 0, 0) {
|
||||
add("-enable-machine-outliner=never", false);
|
||||
}
|
||||
|
||||
match sess.opts.unstable_opts.merge_functions.unwrap_or(sess.target.merge_functions) {
|
||||
MergeFunctions::Disabled | MergeFunctions::Trampolines => {}
|
||||
MergeFunctions::Aliases => {
|
||||
|
@ -343,7 +343,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||
/// operations take `&self`. Use a `RefCell` in `AllocExtra` if you
|
||||
/// need to mutate.
|
||||
#[inline(always)]
|
||||
fn memory_read(
|
||||
fn before_memory_read(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_machine: &Self,
|
||||
_alloc_extra: &Self::AllocExtra,
|
||||
@ -355,7 +355,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||
|
||||
/// Hook for performing extra checks on a memory write access.
|
||||
#[inline(always)]
|
||||
fn memory_written(
|
||||
fn before_memory_write(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_machine: &mut Self,
|
||||
_alloc_extra: &mut Self::AllocExtra,
|
||||
@ -367,7 +367,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||
|
||||
/// Hook for performing extra operations on a memory deallocation.
|
||||
#[inline(always)]
|
||||
fn memory_deallocated(
|
||||
fn before_memory_deallocation(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_machine: &mut Self,
|
||||
_alloc_extra: &mut Self::AllocExtra,
|
||||
|
@ -327,7 +327,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
|
||||
// Let the machine take some extra action
|
||||
let size = alloc.size();
|
||||
M::memory_deallocated(
|
||||
M::before_memory_deallocation(
|
||||
*self.tcx,
|
||||
&mut self.machine,
|
||||
&mut alloc.extra,
|
||||
@ -575,7 +575,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
)?;
|
||||
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
|
||||
let range = alloc_range(offset, size);
|
||||
M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
|
||||
M::before_memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
|
||||
Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
|
||||
} else {
|
||||
// Even in this branch we have to be sure that we actually access the allocation, in
|
||||
@ -641,7 +641,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
|
||||
let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
|
||||
let range = alloc_range(offset, size);
|
||||
M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
|
||||
M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
|
||||
Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id }))
|
||||
} else {
|
||||
Ok(None)
|
||||
@ -1078,7 +1078,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
};
|
||||
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
|
||||
let src_range = alloc_range(src_offset, size);
|
||||
M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_prov), src_range)?;
|
||||
M::before_memory_read(
|
||||
*tcx,
|
||||
&self.machine,
|
||||
&src_alloc.extra,
|
||||
(src_alloc_id, src_prov),
|
||||
src_range,
|
||||
)?;
|
||||
// We need the `dest` ptr for the next operation, so we get it now.
|
||||
// We already did the source checks and called the hooks so we are good to return early.
|
||||
let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else {
|
||||
@ -1103,7 +1109,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// Destination alloc preparations and access hooks.
|
||||
let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
|
||||
let dest_range = alloc_range(dest_offset, size * num_copies);
|
||||
M::memory_written(
|
||||
M::before_memory_write(
|
||||
*tcx,
|
||||
extra,
|
||||
&mut dest_alloc.extra,
|
||||
|
@ -457,6 +457,7 @@ struct HandlerInner {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum StashKey {
|
||||
ItemNoType,
|
||||
UnderscoreForArrayLengths,
|
||||
}
|
||||
|
||||
fn default_track_diagnostic(_: &Diagnostic) {}
|
||||
|
@ -277,7 +277,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
|
||||
ungated!(
|
||||
should_panic, Normal,
|
||||
template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing,
|
||||
template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
|
||||
),
|
||||
// FIXME(Centril): This can be used on stable but shouldn't.
|
||||
ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
|
||||
|
@ -24,17 +24,10 @@ extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer(
|
||||
const char* const Filenames[],
|
||||
size_t FilenamesLen,
|
||||
RustStringRef BufferOut) {
|
||||
#if LLVM_VERSION_GE(13,0)
|
||||
SmallVector<std::string,32> FilenameRefs;
|
||||
for (size_t i = 0; i < FilenamesLen; i++) {
|
||||
FilenameRefs.push_back(std::string(Filenames[i]));
|
||||
}
|
||||
#else
|
||||
SmallVector<StringRef,32> FilenameRefs;
|
||||
for (size_t i = 0; i < FilenamesLen; i++) {
|
||||
FilenameRefs.push_back(StringRef(Filenames[i]));
|
||||
}
|
||||
#endif
|
||||
auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter(
|
||||
makeArrayRef(FilenameRefs));
|
||||
RawRustStringOstream OS(BufferOut);
|
||||
@ -109,9 +102,5 @@ extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) {
|
||||
}
|
||||
|
||||
extern "C" uint32_t LLVMRustCoverageMappingVersion() {
|
||||
#if LLVM_VERSION_GE(13, 0)
|
||||
return coverage::CovMapVersion::Version6;
|
||||
#else
|
||||
return coverage::CovMapVersion::Version5;
|
||||
#endif
|
||||
}
|
||||
|
@ -822,7 +822,8 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
|
||||
LLVMRustSanitizerOptions *SanitizerOptions,
|
||||
const char *PGOGenPath, const char *PGOUsePath,
|
||||
bool InstrumentCoverage, bool InstrumentGCOV,
|
||||
bool InstrumentCoverage, const char *InstrProfileOutput,
|
||||
bool InstrumentGCOV,
|
||||
const char *PGOSampleUsePath, bool DebugInfoForProfiling,
|
||||
void* LlvmSelfProfiler,
|
||||
LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
|
||||
@ -869,19 +870,11 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_GE(13, 0)
|
||||
PassBuilder PB(TM, PTO, PGOOpt, &PIC);
|
||||
LoopAnalysisManager LAM;
|
||||
FunctionAnalysisManager FAM;
|
||||
CGSCCAnalysisManager CGAM;
|
||||
ModuleAnalysisManager MAM;
|
||||
#else
|
||||
PassBuilder PB(DebugPassManager, TM, PTO, PGOOpt, &PIC);
|
||||
LoopAnalysisManager LAM(DebugPassManager);
|
||||
FunctionAnalysisManager FAM(DebugPassManager);
|
||||
CGSCCAnalysisManager CGAM(DebugPassManager);
|
||||
ModuleAnalysisManager MAM(DebugPassManager);
|
||||
#endif
|
||||
|
||||
FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); });
|
||||
|
||||
@ -922,8 +915,11 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
|
||||
if (InstrumentCoverage) {
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
[InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
InstrProfOptions Options;
|
||||
if (InstrProfileOutput) {
|
||||
Options.InstrProfileOutput = InstrProfileOutput;
|
||||
}
|
||||
MPM.addPass(InstrProfiling(Options, false));
|
||||
}
|
||||
);
|
||||
@ -1015,11 +1011,7 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
}
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_GE(13, 0)
|
||||
ModulePassManager MPM;
|
||||
#else
|
||||
ModulePassManager MPM(DebugPassManager);
|
||||
#endif
|
||||
bool NeedThinLTOBufferPasses = UseThinLTOBuffers;
|
||||
if (!NoPrepopulatePasses) {
|
||||
// The pre-link pipelines don't support O0 and require using budilO0DefaultPipeline() instead.
|
||||
@ -1434,17 +1426,13 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
||||
Ret->ResolvedODR[ModuleIdentifier][GUID] = NewLinkage;
|
||||
};
|
||||
|
||||
#if LLVM_VERSION_GE(13,0)
|
||||
// Uses FromPrevailing visibility scheme which works for many binary
|
||||
// formats. We probably could and should use ELF visibility scheme for many of
|
||||
// our targets, however.
|
||||
lto::Config conf;
|
||||
thinLTOResolvePrevailingInIndex(conf, Ret->Index, isPrevailing, recordNewLinkage,
|
||||
Ret->GUIDPreservedSymbols);
|
||||
#else
|
||||
thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage,
|
||||
Ret->GUIDPreservedSymbols);
|
||||
#endif
|
||||
|
||||
// Here we calculate an `ExportedGUIDs` set for use in the `isExported`
|
||||
// callback below. This callback below will dictate the linkage for all
|
||||
// summaries in the index, and we basically just only want to ensure that dead
|
||||
|
@ -413,18 +413,12 @@ LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target,
|
||||
LLVMValueRef Old, LLVMValueRef Source,
|
||||
LLVMAtomicOrdering Order,
|
||||
LLVMAtomicOrdering FailureOrder, LLVMBool Weak) {
|
||||
#if LLVM_VERSION_GE(13,0)
|
||||
// Rust probably knows the alignment of the target value and should be able to
|
||||
// specify something more precise than MaybeAlign here. See also
|
||||
// https://reviews.llvm.org/D97224 which may be a useful reference.
|
||||
AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg(
|
||||
unwrap(Target), unwrap(Old), unwrap(Source), llvm::MaybeAlign(), fromRust(Order),
|
||||
fromRust(FailureOrder));
|
||||
#else
|
||||
AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg(
|
||||
unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order),
|
||||
fromRust(FailureOrder));
|
||||
#endif
|
||||
ACXI->setWeak(Weak);
|
||||
return wrap(ACXI);
|
||||
}
|
||||
@ -472,19 +466,11 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
|
||||
char *Constraints, size_t ConstraintsLen,
|
||||
LLVMBool HasSideEffects, LLVMBool IsAlignStack,
|
||||
LLVMRustAsmDialect Dialect, LLVMBool CanThrow) {
|
||||
#if LLVM_VERSION_GE(13, 0)
|
||||
return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
|
||||
StringRef(AsmString, AsmStringLen),
|
||||
StringRef(Constraints, ConstraintsLen),
|
||||
HasSideEffects, IsAlignStack,
|
||||
fromRust(Dialect), CanThrow));
|
||||
#else
|
||||
return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
|
||||
StringRef(AsmString, AsmStringLen),
|
||||
StringRef(Constraints, ConstraintsLen),
|
||||
HasSideEffects, IsAlignStack,
|
||||
fromRust(Dialect)));
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,
|
||||
@ -1274,10 +1260,8 @@ static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) {
|
||||
return LLVMRustDiagnosticKind::Linker;
|
||||
case DK_Unsupported:
|
||||
return LLVMRustDiagnosticKind::Unsupported;
|
||||
#if LLVM_VERSION_GE(13, 0)
|
||||
case DK_SrcMgr:
|
||||
return LLVMRustDiagnosticKind::SrcMgr;
|
||||
#endif
|
||||
default:
|
||||
return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark)
|
||||
? LLVMRustDiagnosticKind::OptimizationRemarkOther
|
||||
@ -1351,30 +1335,11 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
|
||||
|
||||
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
|
||||
|
||||
#if LLVM_VERSION_LT(13, 0)
|
||||
using LLVMInlineAsmDiagHandlerTy = LLVMContext::InlineAsmDiagHandlerTy;
|
||||
#else
|
||||
using LLVMInlineAsmDiagHandlerTy = void*;
|
||||
#endif
|
||||
|
||||
extern "C" void LLVMRustSetInlineAsmDiagnosticHandler(
|
||||
LLVMContextRef C, LLVMInlineAsmDiagHandlerTy H, void *CX) {
|
||||
// Diagnostic handlers were unified in LLVM change 5de2d189e6ad, so starting
|
||||
// with LLVM 13 this function is gone.
|
||||
#if LLVM_VERSION_LT(13, 0)
|
||||
unwrap(C)->setInlineAsmDiagnosticHandler(H, CX);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(
|
||||
LLVMDiagnosticInfoRef DI, unsigned *Cookie) {
|
||||
#if LLVM_VERSION_GE(13, 0)
|
||||
llvm::DiagnosticInfoSrcMgr *SM = static_cast<llvm::DiagnosticInfoSrcMgr *>(unwrap(DI));
|
||||
*Cookie = SM->getLocCookie();
|
||||
return wrap(&SM->getSMDiag());
|
||||
#else
|
||||
report_fatal_error("Shouldn't get called on older versions");
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef,
|
||||
|
@ -1594,7 +1594,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
||||
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;};
|
||||
let mut span: MultiSpan = vec![loop_span].into();
|
||||
span.push_span_label(loop_span, "this might have zero elements to iterate on");
|
||||
for ret_expr in ret_exprs {
|
||||
const MAXITER: usize = 3;
|
||||
let iter = ret_exprs.iter().take(MAXITER);
|
||||
for ret_expr in iter {
|
||||
span.push_span_label(
|
||||
ret_expr.span,
|
||||
"if the loop doesn't execute, this value would never get returned",
|
||||
@ -1604,6 +1606,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
||||
span,
|
||||
"the function expects a value to always be returned, but loops might run zero times",
|
||||
);
|
||||
if MAXITER < ret_exprs.len() {
|
||||
err.note(&format!(
|
||||
"if the loop doesn't execute, {} other values would never get returned",
|
||||
ret_exprs.len() - MAXITER
|
||||
));
|
||||
}
|
||||
err.help(
|
||||
"return a value for the case when the loop has zero elements to iterate on, or \
|
||||
consider changing the return type to account for that possibility",
|
||||
|
@ -28,7 +28,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{
|
||||
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
|
||||
ErrorGuaranteed,
|
||||
ErrorGuaranteed, StashKey,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
@ -1307,7 +1307,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: expr.span,
|
||||
})
|
||||
};
|
||||
self.tcx.mk_array(element_ty, args.len() as u64)
|
||||
let array_len = args.len() as u64;
|
||||
self.suggest_array_len(expr, array_len);
|
||||
self.tcx.mk_array(element_ty, array_len)
|
||||
}
|
||||
|
||||
fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) {
|
||||
if let Some(parent_hir_id) = self.tcx.hir().find_parent_node(expr.hir_id) {
|
||||
let ty = match self.tcx.hir().find(parent_hir_id) {
|
||||
Some(
|
||||
hir::Node::Local(hir::Local { ty: Some(ty), .. })
|
||||
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. }),
|
||||
) => Some(ty),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(ty) = ty
|
||||
&& let hir::TyKind::Array(_, length) = ty.kind
|
||||
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
||||
&& let Some(span) = self.tcx.hir().opt_span(hir_id)
|
||||
{
|
||||
match self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) {
|
||||
Some(mut err) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider specifying the array length",
|
||||
array_len,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_const_block(
|
||||
@ -1333,10 +1365,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
element: &'tcx hir::Expr<'tcx>,
|
||||
count: &'tcx hir::ArrayLen,
|
||||
expected: Expectation<'tcx>,
|
||||
_expr: &'tcx hir::Expr<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let count = self.array_length_to_const(count);
|
||||
if let Some(count) = count.try_eval_usize(tcx, self.param_env) {
|
||||
self.suggest_array_len(expr, count);
|
||||
}
|
||||
|
||||
let uty = match expected {
|
||||
ExpectHasType(uty) => match *uty.kind() {
|
||||
|
@ -1139,11 +1139,7 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn le(&self, other: &Rhs) -> bool {
|
||||
// Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`.
|
||||
// FIXME: The root cause was fixed upstream in LLVM with:
|
||||
// https://github.com/llvm/llvm-project/commit/9bad7de9a3fb844f1ca2965f35d0c2a3d1e11775
|
||||
// Revert this workaround once support for LLVM 12 gets dropped.
|
||||
!matches!(self.partial_cmp(other), None | Some(Greater))
|
||||
matches!(self.partial_cmp(other), Some(Less | Equal))
|
||||
}
|
||||
|
||||
/// This method tests greater than (for `self` and `other`) and is used by the `>` operator.
|
||||
|
@ -515,11 +515,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
|
||||
let version = output(cmd.arg("--version"));
|
||||
let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
|
||||
if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) {
|
||||
if major >= 12 {
|
||||
if major >= 13 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic!("\n\nbad LLVM version: {}, need >=12.0\n\n", version)
|
||||
panic!("\n\nbad LLVM version: {}, need >=13.0\n\n", version)
|
||||
}
|
||||
|
||||
fn configure_cmake(
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
@ -14,8 +14,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
sudo \
|
||||
gdb \
|
||||
llvm-12-tools \
|
||||
llvm-12-dev \
|
||||
llvm-13-tools \
|
||||
llvm-13-dev \
|
||||
libedit-dev \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
@ -29,7 +29,7 @@ RUN sh /scripts/sccache.sh
|
||||
# using llvm-link-shared due to libffi issues -- see #34486
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--build=x86_64-unknown-linux-gnu \
|
||||
--llvm-root=/usr/lib/llvm-12 \
|
||||
--llvm-root=/usr/lib/llvm-13 \
|
||||
--enable-llvm-link-shared \
|
||||
--set rust.thin-lto-import-instr-limit=10
|
||||
|
||||
@ -41,4 +41,4 @@ ENV SCRIPT python2.7 ../x.py --stage 1 test --exclude src/tools/tidy && \
|
||||
# It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
|
||||
# despite having different output on 32-bit vs 64-bit targets.
|
||||
python2.7 ../x.py --stage 1 test src/test/mir-opt \
|
||||
--host='' --target=i686-unknown-linux-gnu
|
||||
--host='' --target=i686-unknown-linux-gnu
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@ -17,8 +17,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
sudo \
|
||||
gdb \
|
||||
llvm-12-tools \
|
||||
llvm-12-dev \
|
||||
llvm-13-tools \
|
||||
llvm-13-dev \
|
||||
libedit-dev \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
@ -40,7 +40,7 @@ RUN sh /scripts/sccache.sh
|
||||
# using llvm-link-shared due to libffi issues -- see #34486
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--build=x86_64-unknown-linux-gnu \
|
||||
--llvm-root=/usr/lib/llvm-12 \
|
||||
--llvm-root=/usr/lib/llvm-13 \
|
||||
--enable-llvm-link-shared \
|
||||
--set rust.thin-lto-import-instr-limit=10
|
||||
|
@ -284,7 +284,7 @@ jobs:
|
||||
- name: mingw-check
|
||||
<<: *job-linux-xl
|
||||
|
||||
- name: x86_64-gnu-llvm-12
|
||||
- name: x86_64-gnu-llvm-13
|
||||
<<: *job-linux-xl
|
||||
|
||||
- name: x86_64-gnu-tools
|
||||
@ -431,12 +431,12 @@ jobs:
|
||||
- name: x86_64-gnu-distcheck
|
||||
<<: *job-linux-xl
|
||||
|
||||
- name: x86_64-gnu-llvm-12
|
||||
- name: x86_64-gnu-llvm-13
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
<<: *job-linux-xl
|
||||
|
||||
- name: x86_64-gnu-llvm-12-stage1
|
||||
- name: x86_64-gnu-llvm-13-stage1
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
<<: *job-linux-xl
|
||||
|
@ -97,7 +97,17 @@ $ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
|
||||
}
|
||||
```
|
||||
|
||||
After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`:
|
||||
After running this program, a new file named like `default_11699812450447639123_0_20944` should be in the current working directory.
|
||||
A new, unique file name will be generated each time the program is run to avoid overwriting previous data.
|
||||
|
||||
```shell
|
||||
$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
|
||||
...
|
||||
$ ls default_*.profraw
|
||||
default_11699812450447639123_0_20944.profraw
|
||||
```
|
||||
|
||||
You can also set a specific file name or path for the generated `.profraw` files by using the environment variable `LLVM_PROFILE_FILE`:
|
||||
|
||||
```shell
|
||||
$ echo "{some: 'thing'}" \
|
||||
@ -115,6 +125,9 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing
|
||||
- `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`).
|
||||
- `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered.
|
||||
|
||||
In the first example above, the value `11699812450447639123_0` in the generated filename is the instrumented binary's signature,
|
||||
which replaced the `%m` pattern and the value `20944` is the process ID of the binary being executed.
|
||||
|
||||
## Installing LLVM coverage tools
|
||||
|
||||
LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher, and processing the *raw* data may require exactly the LLVM version used by the compiler. (`llvm-cov --version` typically shows the tool's LLVM version number, and `rustc --verbose --version` shows the version of LLVM used by the Rust compiler.)
|
||||
@ -181,11 +194,10 @@ A typical use case for coverage analysis is test coverage. Rust's source-based c
|
||||
|
||||
The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate.
|
||||
|
||||
Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.)
|
||||
Since `cargo test` both builds and runs the tests, we set the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag.
|
||||
|
||||
```shell
|
||||
$ RUSTFLAGS="-C instrument-coverage" \
|
||||
LLVM_PROFILE_FILE="json5format-%m.profraw" \
|
||||
cargo test --tests
|
||||
```
|
||||
|
||||
@ -210,7 +222,7 @@ test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them:
|
||||
|
||||
```shell
|
||||
$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata
|
||||
$ llvm-profdata merge -sparse default_*.profraw -o json5format.profdata
|
||||
```
|
||||
|
||||
Then run the `cov` tool, with the `profdata` file and all test binaries:
|
||||
@ -230,6 +242,8 @@ $ llvm-cov show \
|
||||
--Xdemangler=rustfilt | less -R
|
||||
```
|
||||
|
||||
> **Note**: If overriding the default `profraw` file name via the `LLVM_PROFILE_FILE` environment variable, it's highly recommended to use the `%m` and `%p` special pattern strings to generate unique file names in the case of more than a single test binary being executed.
|
||||
|
||||
> **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_
|
||||
|
||||
### Tips for listing the binaries automatically
|
||||
@ -271,9 +285,8 @@ To include doc tests in the coverage results, drop the `--tests` flag, and apply
|
||||
```bash
|
||||
$ RUSTFLAGS="-C instrument-coverage" \
|
||||
RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
|
||||
LLVM_PROFILE_FILE="json5format-%m.profraw" \
|
||||
cargo test
|
||||
$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata
|
||||
$ llvm-profdata merge -sparse default_*.profraw -o json5format.profdata
|
||||
```
|
||||
|
||||
The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries
|
||||
@ -302,8 +315,7 @@ $ llvm-cov report \
|
||||
> version without doc tests, include:
|
||||
|
||||
- The `cargo test ... --no-run` command is updated with the same environment variables
|
||||
and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE`
|
||||
is only used when _running_ the tests.)
|
||||
and flags used to _build_ the tests, _including_ the doc tests.
|
||||
- The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out`
|
||||
binaries generated for doc tests (note, however, that some `rust_out` files may not
|
||||
be executable binaries).
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Test that PAC instructions are emitted when branch-protection is specified.
|
||||
|
||||
// min-llvm-version: 10.0.1
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: --target aarch64-unknown-linux-gnu
|
||||
// compile-flags: -Z branch-protection=pac-ret,leaf
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: --target avr-unknown-gnu-atmega328
|
||||
// needs-llvm-components: avr
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: --target avr-unknown-gnu-atmega328
|
||||
// needs-llvm-components: avr
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: --target bpfel-unknown-none -C target_feature=+alu32
|
||||
// needs-llvm-components: bpf
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: --target msp430-none-elf
|
||||
// needs-llvm-components: msp430
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 12.0.1
|
||||
// revisions: powerpc powerpc64
|
||||
// assembly-output: emit-asm
|
||||
//[powerpc] compile-flags: --target powerpc-unknown-linux-gnu
|
||||
|
@ -156,7 +156,6 @@
|
||||
// [r74] needs-llvm-components: x86
|
||||
// [r75] compile-flags:--target x86_64-fortanix-unknown-sgx
|
||||
// [r75] needs-llvm-components: x86
|
||||
// [r75] min-llvm-version: 11.0.0
|
||||
// [r76] compile-flags:--target x86_64-fuchsia
|
||||
// [r76] needs-llvm-components: x86
|
||||
// [r77] compile-flags:--target x86_64-linux-android
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0.0
|
||||
// compile-flags: -O
|
||||
// only-x86_64
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
// Code generation of atomic operations for LLVM 12
|
||||
// ignore-llvm-version: 13 - 99
|
||||
// compile-flags: -O
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::sync::atomic::{AtomicI32, Ordering::*};
|
||||
|
||||
// CHECK-LABEL: @compare_exchange
|
||||
#[no_mangle]
|
||||
pub fn compare_exchange(a: &AtomicI32) {
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 10 monotonic monotonic
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 11 acquire acquire
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 12 seq_cst seq_cst
|
||||
let _ = a.compare_exchange(0, 10, Relaxed, Relaxed);
|
||||
let _ = a.compare_exchange(0, 11, Relaxed, Acquire);
|
||||
let _ = a.compare_exchange(0, 12, Relaxed, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 20 release monotonic
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 21 acq_rel acquire
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 22 seq_cst seq_cst
|
||||
let _ = a.compare_exchange(0, 20, Release, Relaxed);
|
||||
let _ = a.compare_exchange(0, 21, Release, Acquire);
|
||||
let _ = a.compare_exchange(0, 22, Release, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 30 acquire monotonic
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 31 acquire acquire
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 32 seq_cst seq_cst
|
||||
let _ = a.compare_exchange(0, 30, Acquire, Relaxed);
|
||||
let _ = a.compare_exchange(0, 31, Acquire, Acquire);
|
||||
let _ = a.compare_exchange(0, 32, Acquire, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 40 acq_rel monotonic
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 41 acq_rel acquire
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 42 seq_cst seq_cst
|
||||
let _ = a.compare_exchange(0, 40, AcqRel, Relaxed);
|
||||
let _ = a.compare_exchange(0, 41, AcqRel, Acquire);
|
||||
let _ = a.compare_exchange(0, 42, AcqRel, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 50 seq_cst monotonic
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 51 seq_cst acquire
|
||||
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 52 seq_cst seq_cst
|
||||
let _ = a.compare_exchange(0, 50, SeqCst, Relaxed);
|
||||
let _ = a.compare_exchange(0, 51, SeqCst, Acquire);
|
||||
let _ = a.compare_exchange(0, 52, SeqCst, SeqCst);
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @compare_exchange_weak
|
||||
#[no_mangle]
|
||||
pub fn compare_exchange_weak(w: &AtomicI32) {
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 10 monotonic monotonic
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 11 acquire acquire
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 12 seq_cst seq_cst
|
||||
let _ = w.compare_exchange_weak(1, 10, Relaxed, Relaxed);
|
||||
let _ = w.compare_exchange_weak(1, 11, Relaxed, Acquire);
|
||||
let _ = w.compare_exchange_weak(1, 12, Relaxed, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 20 release monotonic
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 21 acq_rel acquire
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 22 seq_cst seq_cst
|
||||
let _ = w.compare_exchange_weak(1, 20, Release, Relaxed);
|
||||
let _ = w.compare_exchange_weak(1, 21, Release, Acquire);
|
||||
let _ = w.compare_exchange_weak(1, 22, Release, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 30 acquire monotonic
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 31 acquire acquire
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 32 seq_cst seq_cst
|
||||
let _ = w.compare_exchange_weak(1, 30, Acquire, Relaxed);
|
||||
let _ = w.compare_exchange_weak(1, 31, Acquire, Acquire);
|
||||
let _ = w.compare_exchange_weak(1, 32, Acquire, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 40 acq_rel monotonic
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 41 acq_rel acquire
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 42 seq_cst seq_cst
|
||||
let _ = w.compare_exchange_weak(1, 40, AcqRel, Relaxed);
|
||||
let _ = w.compare_exchange_weak(1, 41, AcqRel, Acquire);
|
||||
let _ = w.compare_exchange_weak(1, 42, AcqRel, SeqCst);
|
||||
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 50 seq_cst monotonic
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 51 seq_cst acquire
|
||||
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 52 seq_cst seq_cst
|
||||
let _ = w.compare_exchange_weak(1, 50, SeqCst, Relaxed);
|
||||
let _ = w.compare_exchange_weak(1, 51, SeqCst, Acquire);
|
||||
let _ = w.compare_exchange_weak(1, 52, SeqCst, SeqCst);
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
// Code generation of atomic operations.
|
||||
// min-llvm-version: 13.0
|
||||
// compile-flags: -O
|
||||
#![crate_type = "lib"]
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Test that the correct module flags are emitted with different branch protection flags.
|
||||
|
||||
// revisions: BTI PACRET LEAF BKEY NONE
|
||||
// min-llvm-version: 12.0.0
|
||||
// needs-llvm-components: aarch64
|
||||
// [BTI] compile-flags: -Z branch-protection=bti
|
||||
// [PACRET] compile-flags: -Z branch-protection=pac-ret
|
||||
|
17
src/test/codegen/instrument-coverage.rs
Normal file
17
src/test/codegen/instrument-coverage.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Test that `-Cinstrument-coverage` creates expected __llvm_profile_filename symbol in LLVM IR.
|
||||
|
||||
// needs-profiler-support
|
||||
// compile-flags: -Cinstrument-coverage
|
||||
|
||||
// CHECK: @__llvm_profile_filename = {{.*}}"default_%m_%p.profraw\00"{{.*}}
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
||||
#[inline(never)]
|
||||
fn some_function() {
|
||||
|
||||
}
|
||||
|
||||
pub fn some_other_function() {
|
||||
some_function();
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
// min-llvm-version: 14.0
|
||||
// revisions: O Os
|
||||
//[Os] compile-flags: -Copt-level=s
|
||||
//[O] compile-flags: -O
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Tests that unwinding from an asm block is caught and forced to abort
|
||||
//! when `-C panic=abort`.
|
||||
|
||||
// min-llvm-version: 13.0.0
|
||||
// only-x86_64
|
||||
// compile-flags: -C panic=abort
|
||||
// no-prefer-dynamic
|
||||
|
14
src/test/ui/array-slice-vec/suggest-array-length.fixed
Normal file
14
src/test/ui/array-slice-vec/suggest-array-length.fixed
Normal file
@ -0,0 +1,14 @@
|
||||
// run-rustfix
|
||||
#![allow(unused_variables, dead_code, non_upper_case_globals)]
|
||||
|
||||
fn main() {
|
||||
const Foo: [i32; 3] = [1, 2, 3];
|
||||
//~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
//~| ERROR using `_` for array lengths is unstable
|
||||
let foo: [i32; 3] = [1, 2, 3];
|
||||
//~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
//~| ERROR using `_` for array lengths is unstable
|
||||
let bar: [i32; 3] = [0; 3];
|
||||
//~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
//~| ERROR using `_` for array lengths is unstable
|
||||
}
|
14
src/test/ui/array-slice-vec/suggest-array-length.rs
Normal file
14
src/test/ui/array-slice-vec/suggest-array-length.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// run-rustfix
|
||||
#![allow(unused_variables, dead_code, non_upper_case_globals)]
|
||||
|
||||
fn main() {
|
||||
const Foo: [i32; _] = [1, 2, 3];
|
||||
//~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
//~| ERROR using `_` for array lengths is unstable
|
||||
let foo: [i32; _] = [1, 2, 3];
|
||||
//~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
//~| ERROR using `_` for array lengths is unstable
|
||||
let bar: [i32; _] = [0; 3];
|
||||
//~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
//~| ERROR using `_` for array lengths is unstable
|
||||
}
|
48
src/test/ui/array-slice-vec/suggest-array-length.stderr
Normal file
48
src/test/ui/array-slice-vec/suggest-array-length.stderr
Normal file
@ -0,0 +1,48 @@
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/suggest-array-length.rs:8:20
|
||||
|
|
||||
LL | let foo: [i32; _] = [1, 2, 3];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/suggest-array-length.rs:11:20
|
||||
|
|
||||
LL | let bar: [i32; _] = [0; 3];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/suggest-array-length.rs:5:22
|
||||
|
|
||||
LL | const Foo: [i32; _] = [1, 2, 3];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/suggest-array-length.rs:5:22
|
||||
|
|
||||
LL | const Foo: [i32; _] = [1, 2, 3];
|
||||
| ^ help: consider specifying the array length: `3`
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/suggest-array-length.rs:8:20
|
||||
|
|
||||
LL | let foo: [i32; _] = [1, 2, 3];
|
||||
| ^ help: consider specifying the array length: `3`
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/suggest-array-length.rs:11:20
|
||||
|
|
||||
LL | let bar: [i32; _] = [0; 3];
|
||||
| ^ help: consider specifying the array length: `3`
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0.0
|
||||
// only-aarch64
|
||||
// run-pass
|
||||
// needs-asm-support
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0.0
|
||||
// run-pass
|
||||
// needs-asm-support
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 13.0.0
|
||||
// only-x86_64
|
||||
// run-pass
|
||||
// needs-asm-support
|
||||
|
@ -1,4 +1,3 @@
|
||||
// min-llvm-version: 12.0.1
|
||||
// only-x86_64
|
||||
// only-linux
|
||||
// needs-asm-support
|
||||
|
@ -9,6 +9,12 @@ LL | async fn new() -> [u8; _];
|
||||
= note: `async` trait functions are not currently supported
|
||||
= note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
|
||||
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/issue-95307.rs:7:28
|
||||
|
|
||||
LL | async fn new() -> [u8; _];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/issue-95307.rs:7:28
|
||||
|
|
||||
@ -18,12 +24,6 @@ LL | async fn new() -> [u8; _];
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/issue-95307.rs:7:28
|
||||
|
|
||||
LL | async fn new() -> [u8; _];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0658, E0706.
|
||||
|
@ -1,33 +1,24 @@
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:11:27
|
||||
|
|
||||
LL | let _x: [u8; 3] = [0; _];
|
||||
| ^
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:11:27
|
||||
|
|
||||
LL | let _x: [u8; 3] = [0; _];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:14:18
|
||||
|
|
||||
LL | let _y: [u8; _] = [0; 3];
|
||||
| ^
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error: in expressions, `_` can only be used on the left-hand side of an assignment
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:14:18
|
||||
|
|
||||
LL | let _y: [u8; _] = [0; 3];
|
||||
| ^ `_` not allowed here
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:14:18
|
||||
|
|
||||
LL | let _y: [u8; _] = [0; 3];
|
||||
| ^ help: consider specifying the array length: `3`
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error[E0747]: type provided when a constant was expected
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:20:20
|
||||
|
|
||||
@ -37,6 +28,15 @@ LL | let _x = foo::<_>([1,2]);
|
||||
= help: const arguments cannot yet be inferred with `_`
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: using `_` for array lengths is unstable
|
||||
--> $DIR/feature-gate-generic_arg_infer.rs:11:27
|
||||
|
|
||||
LL | let _x: [u8; 3] = [0; _];
|
||||
| ^
|
||||
|
|
||||
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
|
||||
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0658, E0747.
|
||||
|
@ -1,5 +1,4 @@
|
||||
// run-pass
|
||||
// min-llvm-version: 13.0
|
||||
// compile-flags: -O
|
||||
|
||||
// Regression test for issue #80309
|
||||
|
@ -1,5 +1,4 @@
|
||||
// run-pass
|
||||
// min-llvm-version: 13.0
|
||||
// compile-flags: -O
|
||||
|
||||
// Regression test for issue #80309
|
||||
|
22
src/test/ui/typeck/issue-100285.rs
Normal file
22
src/test/ui/typeck/issue-100285.rs
Normal file
@ -0,0 +1,22 @@
|
||||
fn foo(n: i32) -> i32 {
|
||||
for i in 0..0 {
|
||||
//~^ ERROR: mismatched types [E0308]
|
||||
if n < 0 {
|
||||
return i;
|
||||
} else if n < 10 {
|
||||
return 1;
|
||||
} else if n < 20 {
|
||||
return 2;
|
||||
} else if n < 30 {
|
||||
return 3;
|
||||
} else if n < 40 {
|
||||
return 4;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
|
||||
}
|
||||
//~| help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
|
||||
}
|
||||
|
||||
fn main() {}
|
34
src/test/ui/typeck/issue-100285.stderr
Normal file
34
src/test/ui/typeck/issue-100285.stderr
Normal file
@ -0,0 +1,34 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-100285.rs:2:5
|
||||
|
|
||||
LL | fn foo(n: i32) -> i32 {
|
||||
| --- expected `i32` because of return type
|
||||
LL | / for i in 0..0 {
|
||||
LL | |
|
||||
LL | | if n < 0 {
|
||||
LL | | return i;
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____^ expected `i32`, found `()`
|
||||
|
|
||||
note: the function expects a value to always be returned, but loops might run zero times
|
||||
--> $DIR/issue-100285.rs:2:5
|
||||
|
|
||||
LL | for i in 0..0 {
|
||||
| ^^^^^^^^^^^^^ this might have zero elements to iterate on
|
||||
...
|
||||
LL | return i;
|
||||
| -------- if the loop doesn't execute, this value would never get returned
|
||||
LL | } else if n < 10 {
|
||||
LL | return 1;
|
||||
| -------- if the loop doesn't execute, this value would never get returned
|
||||
LL | } else if n < 20 {
|
||||
LL | return 2;
|
||||
| -------- if the loop doesn't execute, this value would never get returned
|
||||
= note: if the loop doesn't execute, 3 other values would never get returned
|
||||
= help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Reference in New Issue
Block a user