mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Auto merge of #124675 - matthiaskrgr:rollup-x6n79ua, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #122492 (Implement ptr_as_ref_unchecked) - #123815 (Fix cannot usage in time.rs) - #124059 (default_alloc_error_hook: explain difference to default __rdl_oom in alloc) - #124510 (Add raw identifier in a typo suggestion) - #124555 (coverage: Clean up creation of MC/DC condition bitmaps) - #124593 (Describe and use CStr literals in CStr and CString docs) - #124630 (CI: remove `env-x86_64-apple-tests` YAML anchor) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
befabbc9e5
@ -21,6 +21,8 @@ use rustc_macros::{Decodable_Generic, Encodable_Generic};
|
|||||||
use std::iter::Step;
|
use std::iter::Step;
|
||||||
|
|
||||||
mod layout;
|
mod layout;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
pub use layout::LayoutCalculator;
|
pub use layout::LayoutCalculator;
|
||||||
|
|
||||||
|
7
compiler/rustc_abi/src/tests.rs
Normal file
7
compiler/rustc_abi/src/tests.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn align_constants() {
|
||||||
|
assert_eq!(Align::ONE, Align::from_bytes(1).unwrap());
|
||||||
|
assert_eq!(Align::EIGHT, Align::from_bytes(8).unwrap());
|
||||||
|
}
|
@ -17,7 +17,7 @@ use rustc_data_structures::small_c_str::SmallCStr;
|
|||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||||
use rustc_middle::ty::layout::{
|
use rustc_middle::ty::layout::{
|
||||||
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout,
|
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||||
use rustc_sanitizers::{cfi, kcfi};
|
use rustc_sanitizers::{cfi, kcfi};
|
||||||
@ -27,7 +27,6 @@ use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
|
|||||||
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ffi::CString;
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
@ -1705,13 +1704,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||||||
kcfi_bundle
|
kcfi_bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emits a call to `llvm.instrprof.mcdc.parameters`.
|
||||||
|
///
|
||||||
|
/// This doesn't produce any code directly, but is used as input by
|
||||||
|
/// the LLVM pass that handles coverage instrumentation.
|
||||||
|
///
|
||||||
|
/// (See clang's [`CodeGenPGO::emitMCDCParameters`] for comparison.)
|
||||||
|
///
|
||||||
|
/// [`CodeGenPGO::emitMCDCParameters`]:
|
||||||
|
/// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124
|
||||||
pub(crate) fn mcdc_parameters(
|
pub(crate) fn mcdc_parameters(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &'ll Value,
|
fn_name: &'ll Value,
|
||||||
hash: &'ll Value,
|
hash: &'ll Value,
|
||||||
bitmap_bytes: &'ll Value,
|
bitmap_bytes: &'ll Value,
|
||||||
max_decision_depth: u32,
|
) {
|
||||||
) -> Vec<&'ll Value> {
|
|
||||||
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
|
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
|
||||||
|
|
||||||
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
||||||
@ -1724,8 +1731,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||||||
let args = &[fn_name, hash, bitmap_bytes];
|
let args = &[fn_name, hash, bitmap_bytes];
|
||||||
let args = self.check_call("call", llty, llfn, args);
|
let args = self.check_call("call", llty, llfn, args);
|
||||||
|
|
||||||
let mut cond_bitmaps = vec![];
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = llvm::LLVMRustBuildCall(
|
let _ = llvm::LLVMRustBuildCall(
|
||||||
self.llbuilder,
|
self.llbuilder,
|
||||||
@ -1736,23 +1741,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||||||
[].as_ptr(),
|
[].as_ptr(),
|
||||||
0 as c_uint,
|
0 as c_uint,
|
||||||
);
|
);
|
||||||
// Create condition bitmap named `mcdc.addr`.
|
|
||||||
for i in 0..=max_decision_depth {
|
|
||||||
let mut bx = Builder::with_cx(self.cx);
|
|
||||||
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
|
|
||||||
|
|
||||||
let name = CString::new(format!("mcdc.addr.{i}")).unwrap();
|
|
||||||
let cond_bitmap = {
|
|
||||||
let alloca =
|
|
||||||
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), name.as_ptr());
|
|
||||||
llvm::LLVMSetAlignment(alloca, 4);
|
|
||||||
alloca
|
|
||||||
};
|
|
||||||
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
|
|
||||||
cond_bitmaps.push(cond_bitmap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cond_bitmaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn mcdc_tvbitmap_update(
|
pub(crate) fn mcdc_tvbitmap_update(
|
||||||
@ -1794,8 +1783,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||||||
0 as c_uint,
|
0 as c_uint,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let i32_align = self.tcx().data_layout.i32_align.abi;
|
self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi);
|
||||||
self.store(self.const_i32(0), mcdc_temp, i32_align);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn mcdc_condbitmap_update(
|
pub(crate) fn mcdc_condbitmap_update(
|
||||||
|
@ -13,10 +13,10 @@ use rustc_codegen_ssa::traits::{
|
|||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_llvm::RustString;
|
use rustc_llvm::RustString;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
|
use rustc_middle::mir::coverage::CoverageKind;
|
||||||
use rustc_middle::ty::layout::HasTyCtxt;
|
use rustc_middle::ty::layout::HasTyCtxt;
|
||||||
use rustc_middle::ty::Instance;
|
use rustc_middle::ty::Instance;
|
||||||
use rustc_target::abi::Align;
|
use rustc_target::abi::{Align, Size};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
@ -91,6 +91,42 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||||
|
fn init_coverage(&mut self, instance: Instance<'tcx>) {
|
||||||
|
let Some(function_coverage_info) =
|
||||||
|
self.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If there are no MC/DC bitmaps to set up, return immediately.
|
||||||
|
if function_coverage_info.mcdc_bitmap_bytes == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fn_name = self.get_pgo_func_name_var(instance);
|
||||||
|
let hash = self.const_u64(function_coverage_info.function_source_hash);
|
||||||
|
let bitmap_bytes = self.const_u32(function_coverage_info.mcdc_bitmap_bytes);
|
||||||
|
self.mcdc_parameters(fn_name, hash, bitmap_bytes);
|
||||||
|
|
||||||
|
// Create pointers named `mcdc.addr.{i}` to stack-allocated condition bitmaps.
|
||||||
|
let mut cond_bitmaps = vec![];
|
||||||
|
for i in 0..function_coverage_info.mcdc_num_condition_bitmaps {
|
||||||
|
// MC/DC intrinsics will perform loads/stores that use the ABI default
|
||||||
|
// alignment for i32, so our variable declaration should match.
|
||||||
|
let align = self.tcx.data_layout.i32_align.abi;
|
||||||
|
let cond_bitmap = self.alloca(Size::from_bytes(4), align);
|
||||||
|
llvm::set_value_name(cond_bitmap, format!("mcdc.addr.{i}").as_bytes());
|
||||||
|
self.store(self.const_i32(0), cond_bitmap, align);
|
||||||
|
cond_bitmaps.push(cond_bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.coverage_context()
|
||||||
|
.expect("always present when coverage is enabled")
|
||||||
|
.mcdc_condition_bitmap_map
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(instance, cond_bitmaps);
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||||
// Our caller should have already taken care of inlining subtleties,
|
// Our caller should have already taken care of inlining subtleties,
|
||||||
@ -109,10 +145,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if function_coverage_info.mcdc_bitmap_bytes > 0 {
|
|
||||||
ensure_mcdc_parameters(bx, instance, function_coverage_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(coverage_context) = bx.coverage_context() else { return };
|
let Some(coverage_context) = bx.coverage_context() else { return };
|
||||||
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
||||||
let func_coverage = coverage_map
|
let func_coverage = coverage_map
|
||||||
@ -193,28 +225,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_mcdc_parameters<'ll, 'tcx>(
|
|
||||||
bx: &mut Builder<'_, 'll, 'tcx>,
|
|
||||||
instance: Instance<'tcx>,
|
|
||||||
function_coverage_info: &FunctionCoverageInfo,
|
|
||||||
) {
|
|
||||||
let Some(cx) = bx.coverage_context() else { return };
|
|
||||||
if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
|
||||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
|
||||||
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
|
|
||||||
let max_decision_depth = function_coverage_info.mcdc_max_decision_depth;
|
|
||||||
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes, max_decision_depth as u32);
|
|
||||||
bx.coverage_context()
|
|
||||||
.expect("already checked above")
|
|
||||||
.mcdc_condition_bitmap_map
|
|
||||||
.borrow_mut()
|
|
||||||
.insert(instance, cond_bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
|
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
|
||||||
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
|
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
|
||||||
/// containing the function name, with the specific variable name and linkage
|
/// containing the function name, with the specific variable name and linkage
|
||||||
|
@ -259,6 +259,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
// Apply debuginfo to the newly allocated locals.
|
// Apply debuginfo to the newly allocated locals.
|
||||||
fx.debug_introduce_locals(&mut start_bx);
|
fx.debug_introduce_locals(&mut start_bx);
|
||||||
|
|
||||||
|
// If the backend supports coverage, and coverage is enabled for this function,
|
||||||
|
// do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps).
|
||||||
|
start_bx.init_coverage(instance);
|
||||||
|
|
||||||
// The builders will be created separately for each basic block at `codegen_block`.
|
// The builders will be created separately for each basic block at `codegen_block`.
|
||||||
// So drop the builder of `start_llbb` to avoid having two at the same time.
|
// So drop the builder of `start_llbb` to avoid having two at the same time.
|
||||||
drop(start_bx);
|
drop(start_bx);
|
||||||
|
@ -3,6 +3,11 @@ use rustc_middle::mir::coverage::CoverageKind;
|
|||||||
use rustc_middle::ty::Instance;
|
use rustc_middle::ty::Instance;
|
||||||
|
|
||||||
pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
|
pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
|
||||||
|
/// Performs any start-of-function codegen needed for coverage instrumentation.
|
||||||
|
///
|
||||||
|
/// Can be a no-op in backends that don't support coverage instrumentation.
|
||||||
|
fn init_coverage(&mut self, _instance: Instance<'tcx>) {}
|
||||||
|
|
||||||
/// Handle the MIR coverage info in a backend-specific way.
|
/// Handle the MIR coverage info in a backend-specific way.
|
||||||
///
|
///
|
||||||
/// This can potentially be a no-op in backends that don't support
|
/// This can potentially be a no-op in backends that don't support
|
||||||
|
@ -277,7 +277,7 @@ pub struct FunctionCoverageInfo {
|
|||||||
pub mappings: Vec<Mapping>,
|
pub mappings: Vec<Mapping>,
|
||||||
/// The depth of the deepest decision is used to know how many
|
/// The depth of the deepest decision is used to know how many
|
||||||
/// temp condbitmaps should be allocated for the function.
|
/// temp condbitmaps should be allocated for the function.
|
||||||
pub mcdc_max_decision_depth: u16,
|
pub mcdc_num_condition_bitmaps: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
|
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
|
||||||
|
@ -102,7 +102,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
|
|||||||
|
|
||||||
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
|
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
|
||||||
|
|
||||||
let mcdc_max_decision_depth = coverage_spans
|
let mcdc_num_condition_bitmaps = coverage_spans
|
||||||
.mappings
|
.mappings
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|bcb_mapping| match bcb_mapping.kind {
|
.filter_map(|bcb_mapping| match bcb_mapping.kind {
|
||||||
@ -110,7 +110,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.map_or(0, |max| usize::from(max) + 1);
|
||||||
|
|
||||||
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
||||||
function_source_hash: hir_info.function_source_hash,
|
function_source_hash: hir_info.function_source_hash,
|
||||||
@ -118,7 +118,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
|
|||||||
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
|
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
|
||||||
expressions: coverage_counters.into_expressions(),
|
expressions: coverage_counters.into_expressions(),
|
||||||
mappings,
|
mappings,
|
||||||
mcdc_max_decision_depth,
|
mcdc_num_condition_bitmaps,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1617,7 +1617,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||||||
let post = format!(", consider renaming `{}` into `{snippet}`", suggestion.candidate);
|
let post = format!(", consider renaming `{}` into `{snippet}`", suggestion.candidate);
|
||||||
(span, snippet, post)
|
(span, snippet, post)
|
||||||
} else {
|
} else {
|
||||||
(span, suggestion.candidate.to_string(), String::new())
|
(span, suggestion.candidate.to_ident_string(), String::new())
|
||||||
};
|
};
|
||||||
let msg = match suggestion.target {
|
let msg = match suggestion.target {
|
||||||
SuggestionTarget::SimilarlyNamed => format!(
|
SuggestionTarget::SimilarlyNamed => format!(
|
||||||
|
@ -41,6 +41,7 @@ use crate::sync::Arc;
|
|||||||
/// or anything that implements <code>[Into]<[Vec]<[u8]>></code> (for
|
/// or anything that implements <code>[Into]<[Vec]<[u8]>></code> (for
|
||||||
/// example, you can build a `CString` straight out of a [`String`] or
|
/// example, you can build a `CString` straight out of a [`String`] or
|
||||||
/// a <code>&[str]</code>, since both implement that trait).
|
/// a <code>&[str]</code>, since both implement that trait).
|
||||||
|
/// You can create a `CString` from a literal with `CString::from(c"Text")`.
|
||||||
///
|
///
|
||||||
/// The [`CString::new`] method will actually check that the provided <code>&[[u8]]</code>
|
/// The [`CString::new`] method will actually check that the provided <code>&[[u8]]</code>
|
||||||
/// does not have 0 bytes in the middle, and return an error if it
|
/// does not have 0 bytes in the middle, and return an error if it
|
||||||
@ -1069,27 +1070,22 @@ impl CStr {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Calling `to_string_lossy` on a `CStr` containing valid UTF-8:
|
/// Calling `to_string_lossy` on a `CStr` containing valid UTF-8. The leading
|
||||||
|
/// `c` on the string literal denotes a `CStr`.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::borrow::Cow;
|
/// use std::borrow::Cow;
|
||||||
/// use std::ffi::CStr;
|
|
||||||
///
|
///
|
||||||
/// let cstr = CStr::from_bytes_with_nul(b"Hello World\0")
|
/// assert_eq!(c"Hello World".to_string_lossy(), Cow::Borrowed("Hello World"));
|
||||||
/// .expect("CStr::from_bytes_with_nul failed");
|
|
||||||
/// assert_eq!(cstr.to_string_lossy(), Cow::Borrowed("Hello World"));
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Calling `to_string_lossy` on a `CStr` containing invalid UTF-8:
|
/// Calling `to_string_lossy` on a `CStr` containing invalid UTF-8:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::borrow::Cow;
|
/// use std::borrow::Cow;
|
||||||
/// use std::ffi::CStr;
|
|
||||||
///
|
///
|
||||||
/// let cstr = CStr::from_bytes_with_nul(b"Hello \xF0\x90\x80World\0")
|
|
||||||
/// .expect("CStr::from_bytes_with_nul failed");
|
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cstr.to_string_lossy(),
|
/// c"Hello \xF0\x90\x80World".to_string_lossy(),
|
||||||
/// Cow::Owned(String::from("Hello <20>World")) as Cow<'_, str>
|
/// Cow::Owned(String::from("Hello <20>World")) as Cow<'_, str>
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -23,28 +23,32 @@ use crate::str;
|
|||||||
///
|
///
|
||||||
/// This type represents a borrowed reference to a nul-terminated
|
/// This type represents a borrowed reference to a nul-terminated
|
||||||
/// array of bytes. It can be constructed safely from a <code>&[[u8]]</code>
|
/// array of bytes. It can be constructed safely from a <code>&[[u8]]</code>
|
||||||
/// slice, or unsafely from a raw `*const c_char`. It can then be
|
/// slice, or unsafely from a raw `*const c_char`. It can be expressed as a
|
||||||
/// converted to a Rust <code>&[str]</code> by performing UTF-8 validation, or
|
/// literal in the form `c"Hello world"`.
|
||||||
/// into an owned `CString`.
|
///
|
||||||
|
/// The `CStr` can then be converted to a Rust <code>&[str]</code> by performing
|
||||||
|
/// UTF-8 validation, or into an owned `CString`.
|
||||||
///
|
///
|
||||||
/// `&CStr` is to `CString` as <code>&[str]</code> is to `String`: the former
|
/// `&CStr` is to `CString` as <code>&[str]</code> is to `String`: the former
|
||||||
/// in each pair are borrowed references; the latter are owned
|
/// in each pair are borrowed references; the latter are owned
|
||||||
/// strings.
|
/// strings.
|
||||||
///
|
///
|
||||||
/// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)`
|
/// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)`
|
||||||
/// notwithstanding) and is not recommended to be placed in the signatures of FFI functions.
|
/// notwithstanding) and should not be placed in the signatures of FFI functions.
|
||||||
/// Instead, safe wrappers of FFI functions may leverage the unsafe [`CStr::from_ptr`] constructor
|
/// Instead, safe wrappers of FFI functions may leverage [`CStr::as_ptr`] and the unsafe
|
||||||
/// to provide a safe interface to other consumers.
|
/// [`CStr::from_ptr`] constructor to provide a safe interface to other consumers.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Inspecting a foreign C string:
|
/// Inspecting a foreign C string:
|
||||||
///
|
///
|
||||||
/// ```ignore (extern-declaration)
|
/// ```
|
||||||
/// use std::ffi::CStr;
|
/// use std::ffi::CStr;
|
||||||
/// use std::os::raw::c_char;
|
/// use std::os::raw::c_char;
|
||||||
///
|
///
|
||||||
|
/// # /* Extern functions are awkward in doc comments - fake it instead
|
||||||
/// extern "C" { fn my_string() -> *const c_char; }
|
/// extern "C" { fn my_string() -> *const c_char; }
|
||||||
|
/// # */ unsafe extern "C" fn my_string() -> *const c_char { c"hello".as_ptr() }
|
||||||
///
|
///
|
||||||
/// unsafe {
|
/// unsafe {
|
||||||
/// let slice = CStr::from_ptr(my_string());
|
/// let slice = CStr::from_ptr(my_string());
|
||||||
@ -54,12 +58,14 @@ use crate::str;
|
|||||||
///
|
///
|
||||||
/// Passing a Rust-originating C string:
|
/// Passing a Rust-originating C string:
|
||||||
///
|
///
|
||||||
/// ```ignore (extern-declaration)
|
/// ```
|
||||||
/// use std::ffi::{CString, CStr};
|
/// use std::ffi::{CString, CStr};
|
||||||
/// use std::os::raw::c_char;
|
/// use std::os::raw::c_char;
|
||||||
///
|
///
|
||||||
/// fn work(data: &CStr) {
|
/// fn work(data: &CStr) {
|
||||||
|
/// # /* Extern functions are awkward in doc comments - fake it instead
|
||||||
/// extern "C" { fn work_with(data: *const c_char); }
|
/// extern "C" { fn work_with(data: *const c_char); }
|
||||||
|
/// # */ unsafe extern "C" fn work_with(s: *const c_char) {}
|
||||||
///
|
///
|
||||||
/// unsafe { work_with(data.as_ptr()) }
|
/// unsafe { work_with(data.as_ptr()) }
|
||||||
/// }
|
/// }
|
||||||
@ -70,11 +76,13 @@ use crate::str;
|
|||||||
///
|
///
|
||||||
/// Converting a foreign C string into a Rust `String`:
|
/// Converting a foreign C string into a Rust `String`:
|
||||||
///
|
///
|
||||||
/// ```ignore (extern-declaration)
|
/// ```
|
||||||
/// use std::ffi::CStr;
|
/// use std::ffi::CStr;
|
||||||
/// use std::os::raw::c_char;
|
/// use std::os::raw::c_char;
|
||||||
///
|
///
|
||||||
|
/// # /* Extern functions are awkward in doc comments - fake it instead
|
||||||
/// extern "C" { fn my_string() -> *const c_char; }
|
/// extern "C" { fn my_string() -> *const c_char; }
|
||||||
|
/// # */ unsafe extern "C" fn my_string() -> *const c_char { c"hello".as_ptr() }
|
||||||
///
|
///
|
||||||
/// fn my_string_safe() -> String {
|
/// fn my_string_safe() -> String {
|
||||||
/// let cstr = unsafe { CStr::from_ptr(my_string()) };
|
/// let cstr = unsafe { CStr::from_ptr(my_string()) };
|
||||||
@ -241,16 +249,16 @@ impl CStr {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```ignore (extern-declaration)
|
/// ```
|
||||||
/// use std::ffi::{c_char, CStr};
|
/// use std::ffi::{c_char, CStr};
|
||||||
///
|
///
|
||||||
/// extern "C" {
|
/// fn my_string() -> *const c_char {
|
||||||
/// fn my_string() -> *const c_char;
|
/// c"hello".as_ptr()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// unsafe {
|
/// unsafe {
|
||||||
/// let slice = CStr::from_ptr(my_string());
|
/// let slice = CStr::from_ptr(my_string());
|
||||||
/// println!("string returned: {}", slice.to_str().unwrap());
|
/// assert_eq!(slice.to_str().unwrap(), "hello");
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -264,6 +272,8 @@ impl CStr {
|
|||||||
/// BYTES.as_ptr().cast()
|
/// BYTES.as_ptr().cast()
|
||||||
/// };
|
/// };
|
||||||
/// const HELLO: &CStr = unsafe { CStr::from_ptr(HELLO_PTR) };
|
/// const HELLO: &CStr = unsafe { CStr::from_ptr(HELLO_PTR) };
|
||||||
|
///
|
||||||
|
/// assert_eq!(c"Hello, world!", HELLO);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [valid]: core::ptr#safety
|
/// [valid]: core::ptr#safety
|
||||||
@ -549,6 +559,7 @@ impl CStr {
|
|||||||
///
|
///
|
||||||
/// let empty_cstr = CStr::from_bytes_with_nul(b"\0")?;
|
/// let empty_cstr = CStr::from_bytes_with_nul(b"\0")?;
|
||||||
/// assert!(empty_cstr.is_empty());
|
/// assert!(empty_cstr.is_empty());
|
||||||
|
/// assert!(c"".is_empty());
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -358,6 +358,54 @@ impl<T: ?Sized> *const T {
|
|||||||
if self.is_null() { None } else { unsafe { Some(&*self) } }
|
if self.is_null() { None } else { unsafe { Some(&*self) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a shared reference to the value behind the pointer.
|
||||||
|
/// If the pointer may be null or the value may be uninitialized, [`as_uninit_ref`] must be used instead.
|
||||||
|
/// If the pointer may be null, but the value is known to have been initialized, [`as_ref`] must be used instead.
|
||||||
|
///
|
||||||
|
/// [`as_ref`]: #method.as_ref
|
||||||
|
/// [`as_uninit_ref`]: #method.as_uninit_ref
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// When calling this method, you have to ensure that all of the following is true:
|
||||||
|
///
|
||||||
|
/// * The pointer must be properly aligned.
|
||||||
|
///
|
||||||
|
/// * It must be "dereferenceable" in the sense defined in [the module documentation].
|
||||||
|
///
|
||||||
|
/// * The pointer must point to an initialized instance of `T`.
|
||||||
|
///
|
||||||
|
/// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is
|
||||||
|
/// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data.
|
||||||
|
/// In particular, while this reference exists, the memory the pointer points to must
|
||||||
|
/// not get mutated (except inside `UnsafeCell`).
|
||||||
|
///
|
||||||
|
/// This applies even if the result of this method is unused!
|
||||||
|
/// (The part about being initialized is not yet fully decided, but until
|
||||||
|
/// it is, the only safe approach is to ensure that they are indeed initialized.)
|
||||||
|
///
|
||||||
|
/// [the module documentation]: crate::ptr#safety
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(ptr_as_ref_unchecked)]
|
||||||
|
/// let ptr: *const u8 = &10u8 as *const u8;
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// println!("We got back the value: {}!", ptr.as_ref_unchecked());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
// FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized.
|
||||||
|
#[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")]
|
||||||
|
#[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T {
|
||||||
|
// SAFETY: the caller must guarantee that `self` is valid for a reference
|
||||||
|
unsafe { &*self }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `None` if the pointer is null, or else returns a shared reference to
|
/// Returns `None` if the pointer is null, or else returns a shared reference to
|
||||||
/// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require
|
/// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require
|
||||||
/// that the value has to be initialized.
|
/// that the value has to be initialized.
|
||||||
|
@ -367,6 +367,57 @@ impl<T: ?Sized> *mut T {
|
|||||||
if self.is_null() { None } else { unsafe { Some(&*self) } }
|
if self.is_null() { None } else { unsafe { Some(&*self) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a shared reference to the value behind the pointer.
|
||||||
|
/// If the pointer may be null or the value may be uninitialized, [`as_uninit_ref`] must be used instead.
|
||||||
|
/// If the pointer may be null, but the value is known to have been initialized, [`as_ref`] must be used instead.
|
||||||
|
///
|
||||||
|
/// For the mutable counterpart see [`as_mut_unchecked`].
|
||||||
|
///
|
||||||
|
/// [`as_ref`]: #method.as_ref
|
||||||
|
/// [`as_uninit_ref`]: #method.as_uninit_ref
|
||||||
|
/// [`as_mut_unchecked`]: #method.as_mut_unchecked
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// When calling this method, you have to ensure that all of the following is true:
|
||||||
|
///
|
||||||
|
/// * The pointer must be properly aligned.
|
||||||
|
///
|
||||||
|
/// * It must be "dereferenceable" in the sense defined in [the module documentation].
|
||||||
|
///
|
||||||
|
/// * The pointer must point to an initialized instance of `T`.
|
||||||
|
///
|
||||||
|
/// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is
|
||||||
|
/// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data.
|
||||||
|
/// In particular, while this reference exists, the memory the pointer points to must
|
||||||
|
/// not get mutated (except inside `UnsafeCell`).
|
||||||
|
///
|
||||||
|
/// This applies even if the result of this method is unused!
|
||||||
|
/// (The part about being initialized is not yet fully decided, but until
|
||||||
|
/// it is, the only safe approach is to ensure that they are indeed initialized.)
|
||||||
|
///
|
||||||
|
/// [the module documentation]: crate::ptr#safety
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(ptr_as_ref_unchecked)]
|
||||||
|
/// let ptr: *mut u8 = &mut 10u8 as *mut u8;
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// println!("We got back the value: {}!", ptr.as_ref_unchecked());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
// FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized.
|
||||||
|
#[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")]
|
||||||
|
#[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T {
|
||||||
|
// SAFETY: the caller must guarantee that `self` is valid for a reference
|
||||||
|
unsafe { &*self }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `None` if the pointer is null, or else returns a shared reference to
|
/// Returns `None` if the pointer is null, or else returns a shared reference to
|
||||||
/// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require
|
/// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require
|
||||||
/// that the value has to be initialized.
|
/// that the value has to be initialized.
|
||||||
@ -688,6 +739,58 @@ impl<T: ?Sized> *mut T {
|
|||||||
if self.is_null() { None } else { unsafe { Some(&mut *self) } }
|
if self.is_null() { None } else { unsafe { Some(&mut *self) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a unique reference to the value behind the pointer.
|
||||||
|
/// If the pointer may be null or the value may be uninitialized, [`as_uninit_mut`] must be used instead.
|
||||||
|
/// If the pointer may be null, but the value is known to have been initialized, [`as_mut`] must be used instead.
|
||||||
|
///
|
||||||
|
/// For the shared counterpart see [`as_ref_unchecked`].
|
||||||
|
///
|
||||||
|
/// [`as_mut`]: #method.as_mut
|
||||||
|
/// [`as_uninit_mut`]: #method.as_uninit_mut
|
||||||
|
/// [`as_ref_unchecked`]: #method.as_mut_unchecked
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// When calling this method, you have to ensure that all of the following is true:
|
||||||
|
///
|
||||||
|
/// * The pointer must be properly aligned.
|
||||||
|
///
|
||||||
|
/// * It must be "dereferenceable" in the sense defined in [the module documentation].
|
||||||
|
///
|
||||||
|
/// * The pointer must point to an initialized instance of `T`.
|
||||||
|
///
|
||||||
|
/// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is
|
||||||
|
/// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data.
|
||||||
|
/// In particular, while this reference exists, the memory the pointer points to must
|
||||||
|
/// not get mutated (except inside `UnsafeCell`).
|
||||||
|
///
|
||||||
|
/// This applies even if the result of this method is unused!
|
||||||
|
/// (The part about being initialized is not yet fully decided, but until
|
||||||
|
/// it is, the only safe approach is to ensure that they are indeed initialized.)
|
||||||
|
///
|
||||||
|
/// [the module documentation]: crate::ptr#safety
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(ptr_as_ref_unchecked)]
|
||||||
|
/// let mut s = [1, 2, 3];
|
||||||
|
/// let ptr: *mut u32 = s.as_mut_ptr();
|
||||||
|
/// let first_value = unsafe { ptr.as_mut_unchecked() };
|
||||||
|
/// *first_value = 4;
|
||||||
|
/// # assert_eq!(s, [4, 2, 3]);
|
||||||
|
/// println!("{s:?}"); // It'll print: "[4, 2, 3]".
|
||||||
|
/// ```
|
||||||
|
// FIXME: mention it in the docs for `as_mut` and `as_uninit_mut` once stabilized.
|
||||||
|
#[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")]
|
||||||
|
#[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T {
|
||||||
|
// SAFETY: the caller must guarantee that `self` is valid for a reference
|
||||||
|
unsafe { &mut *self }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `None` if the pointer is null, or else returns a unique reference to
|
/// Returns `None` if the pointer is null, or else returns a unique reference to
|
||||||
/// the value wrapped in `Some`. In contrast to [`as_mut`], this does not require
|
/// the value wrapped in `Some`. In contrast to [`as_mut`], this does not require
|
||||||
/// that the value has to be initialized.
|
/// that the value has to be initialized.
|
||||||
|
@ -1437,10 +1437,10 @@ impl TryFromFloatSecsError {
|
|||||||
const fn description(&self) -> &'static str {
|
const fn description(&self) -> &'static str {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
TryFromFloatSecsErrorKind::Negative => {
|
TryFromFloatSecsErrorKind::Negative => {
|
||||||
"can not convert float seconds to Duration: value is negative"
|
"cannot convert float seconds to Duration: value is negative"
|
||||||
}
|
}
|
||||||
TryFromFloatSecsErrorKind::OverflowOrNan => {
|
TryFromFloatSecsErrorKind::OverflowOrNan => {
|
||||||
"can not convert float seconds to Duration: value is either too big or NaN"
|
"cannot convert float seconds to Duration: value is either too big or NaN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,6 +353,12 @@ fn default_alloc_error_hook(layout: Layout) {
|
|||||||
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
|
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
|
||||||
panic!("memory allocation of {} bytes failed", layout.size());
|
panic!("memory allocation of {} bytes failed", layout.size());
|
||||||
} else {
|
} else {
|
||||||
|
// This is the default path taken on OOM, and the only path taken on stable with std.
|
||||||
|
// Crucially, it does *not* call any user-defined code, and therefore users do not have to
|
||||||
|
// worry about allocation failure causing reentrancy issues. That makes it different from
|
||||||
|
// the default `__rdl_oom` defined in alloc (i.e., the default alloc error handler that is
|
||||||
|
// called when there is no `#[alloc_error_handler]`), which triggers a regular panic and
|
||||||
|
// thus can invoke a user-defined panic hook, executing arbitrary user-defined code.
|
||||||
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
|
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,17 @@ runners:
|
|||||||
os: [ self-hosted, ARM64, linux ]
|
os: [ self-hosted, ARM64, linux ]
|
||||||
|
|
||||||
envs:
|
envs:
|
||||||
|
env-x86_64-apple-tests: &env-x86_64-apple-tests
|
||||||
|
SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps
|
||||||
|
RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
|
||||||
|
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||||
|
MACOSX_DEPLOYMENT_TARGET: 10.12
|
||||||
|
MACOSX_STD_DEPLOYMENT_TARGET: 10.12
|
||||||
|
SELECT_XCODE: /Applications/Xcode_14.3.1.app
|
||||||
|
NO_LLVM_ASSERTIONS: 1
|
||||||
|
NO_DEBUG_ASSERTIONS: 1
|
||||||
|
NO_OVERFLOW_CHECKS: 1
|
||||||
|
|
||||||
production:
|
production:
|
||||||
&production
|
&production
|
||||||
DEPLOY_BUCKET: rust-lang-ci2
|
DEPLOY_BUCKET: rust-lang-ci2
|
||||||
@ -272,16 +283,8 @@ auto:
|
|||||||
<<: *job-macos-xl
|
<<: *job-macos-xl
|
||||||
|
|
||||||
- image: x86_64-apple-1
|
- image: x86_64-apple-1
|
||||||
env: &env-x86_64-apple-tests
|
env:
|
||||||
SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps
|
<<: *env-x86_64-apple-tests
|
||||||
RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
|
|
||||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
|
||||||
MACOSX_DEPLOYMENT_TARGET: 10.12
|
|
||||||
MACOSX_STD_DEPLOYMENT_TARGET: 10.12
|
|
||||||
SELECT_XCODE: /Applications/Xcode_14.3.1.app
|
|
||||||
NO_LLVM_ASSERTIONS: 1
|
|
||||||
NO_DEBUG_ASSERTIONS: 1
|
|
||||||
NO_OVERFLOW_CHECKS: 1
|
|
||||||
<<: *job-macos-xl
|
<<: *job-macos-xl
|
||||||
|
|
||||||
- image: x86_64-apple-2
|
- image: x86_64-apple-2
|
||||||
|
11
tests/ui/span/suggestion-raw-68962.rs
Normal file
11
tests/ui/span/suggestion-raw-68962.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fn r#fn() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let r#final = 1;
|
||||||
|
|
||||||
|
// Should correctly suggest variable defined using raw identifier.
|
||||||
|
fina; //~ ERROR cannot find value
|
||||||
|
|
||||||
|
// Should correctly suggest function defined using raw identifier.
|
||||||
|
f(); //~ ERROR cannot find function
|
||||||
|
}
|
18
tests/ui/span/suggestion-raw-68962.stderr
Normal file
18
tests/ui/span/suggestion-raw-68962.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
error[E0425]: cannot find value `fina` in this scope
|
||||||
|
--> $DIR/suggestion-raw-68962.rs:7:5
|
||||||
|
|
|
||||||
|
LL | fina;
|
||||||
|
| ^^^^ help: a local variable with a similar name exists: `r#final`
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `f` in this scope
|
||||||
|
--> $DIR/suggestion-raw-68962.rs:10:5
|
||||||
|
|
|
||||||
|
LL | fn r#fn() {}
|
||||||
|
| --------- similarly named function `r#fn` defined here
|
||||||
|
...
|
||||||
|
LL | f();
|
||||||
|
| ^ help: a function with a similar name exists: `r#fn`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0425`.
|
Loading…
Reference in New Issue
Block a user