mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Auto merge of #82045 - Dylan-DPC:rollup-244l0sb, r=Dylan-DPC
Rollup of 10 pull requests Successful merges: - #79775 (Fix injected errors when running doctests on a crate named after a keyword) - #81012 (Stabilize the partition_point feature) - #81479 (Allow casting mut array ref to mut ptr) - #81506 (HWAddressSanitizer support) - #81741 (Increment `self.index` before calling `Iterator::self.a.__iterator_ge…) - #81850 (use RWlock when accessing os::env) - #81911 (GAT/const_generics: Allow with_opt_const_param to return GAT param def_id) - #82022 (Push a `char` instead of a `str` with len one into a String) - #82023 (Remove unnecessary lint allow attrs on example) - #82030 (Use `Iterator::all` instead of open-coding it) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
21cbbdc44d
@ -53,6 +53,9 @@ pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll V
|
||||
if enabled.contains(SanitizerSet::THREAD) {
|
||||
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
|
||||
}
|
||||
if enabled.contains(SanitizerSet::HWADDRESS) {
|
||||
llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
|
||||
|
@ -440,6 +440,8 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
|
||||
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
|
||||
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
|
||||
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
|
||||
sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@ -652,6 +654,10 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static
|
||||
if config.sanitizer.contains(SanitizerSet::THREAD) {
|
||||
passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
|
||||
}
|
||||
if config.sanitizer.contains(SanitizerSet::HWADDRESS) {
|
||||
let recover = config.sanitizer_recover.contains(SanitizerSet::HWADDRESS);
|
||||
passes.push(llvm::LLVMRustCreateHWAddressSanitizerPass(recover));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn link(
|
||||
|
@ -131,6 +131,7 @@ pub enum Attribute {
|
||||
ReturnsTwice = 25,
|
||||
ReadNone = 26,
|
||||
InaccessibleMemOnly = 27,
|
||||
SanitizeHWAddress = 28,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
@ -439,6 +440,8 @@ pub struct SanitizerOptions {
|
||||
pub sanitize_memory_recover: bool,
|
||||
pub sanitize_memory_track_origins: c_int,
|
||||
pub sanitize_thread: bool,
|
||||
pub sanitize_hwaddress: bool,
|
||||
pub sanitize_hwaddress_recover: bool,
|
||||
}
|
||||
|
||||
/// LLVMRelocMode
|
||||
@ -2128,6 +2131,7 @@ extern "C" {
|
||||
Recover: bool,
|
||||
) -> &'static mut Pass;
|
||||
pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass;
|
||||
pub fn LLVMRustCreateHWAddressSanitizerPass(Recover: bool) -> &'static mut Pass;
|
||||
pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass);
|
||||
pub fn LLVMRustAddLastExtensionPasses(
|
||||
PMB: &PassManagerBuilder,
|
||||
|
@ -893,6 +893,9 @@ fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linke
|
||||
if sanitizer.contains(SanitizerSet::THREAD) {
|
||||
link_sanitizer_runtime(sess, linker, "tsan");
|
||||
}
|
||||
if sanitizer.contains(SanitizerSet::HWADDRESS) {
|
||||
link_sanitizer_runtime(sess, linker, "hwasan");
|
||||
}
|
||||
}
|
||||
|
||||
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
|
||||
|
@ -85,6 +85,7 @@ enum LLVMRustAttribute {
|
||||
ReturnsTwice = 25,
|
||||
ReadNone = 26,
|
||||
InaccessibleMemOnly = 27,
|
||||
SanitizeHWAddress = 28,
|
||||
};
|
||||
|
||||
typedef struct OpaqueRustString *RustStringRef;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
|
||||
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
|
||||
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
|
||||
|
||||
@ -133,6 +134,12 @@ extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
|
||||
return wrap(createThreadSanitizerLegacyPassPass());
|
||||
}
|
||||
|
||||
extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) {
|
||||
const bool CompileKernel = false;
|
||||
|
||||
return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover));
|
||||
}
|
||||
|
||||
extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
|
||||
assert(RustPass);
|
||||
Pass *Pass = unwrap(RustPass);
|
||||
@ -722,6 +729,8 @@ struct LLVMRustSanitizerOptions {
|
||||
bool SanitizeMemoryRecover;
|
||||
int SanitizeMemoryTrackOrigins;
|
||||
bool SanitizeThread;
|
||||
bool SanitizeHWAddress;
|
||||
bool SanitizeHWAddressRecover;
|
||||
};
|
||||
|
||||
extern "C" void
|
||||
@ -886,6 +895,23 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
|
||||
}
|
||||
);
|
||||
#endif
|
||||
}
|
||||
if (SanitizerOptions->SanitizeHWAddress) {
|
||||
#if LLVM_VERSION_GE(11, 0)
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
|
||||
MPM.addPass(HWAddressSanitizerPass(
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover));
|
||||
}
|
||||
);
|
||||
#else
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[SanitizerOptions](ModulePassManager &MPM) {
|
||||
MPM.addPass(HWAddressSanitizerPass(
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover));
|
||||
}
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
|
||||
return Attribute::ReadNone;
|
||||
case InaccessibleMemOnly:
|
||||
return Attribute::InaccessibleMemOnly;
|
||||
case SanitizeHWAddress:
|
||||
return Attribute::SanitizeHWAddress;
|
||||
}
|
||||
report_fatal_error("bad AttributeKind");
|
||||
}
|
||||
|
@ -2191,19 +2191,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
CastKind::Pointer(PointerCast::ArrayToPointer) => {
|
||||
let ty_from = op.ty(body, tcx);
|
||||
|
||||
let opt_ty_elem = match ty_from.kind() {
|
||||
ty::RawPtr(ty::TypeAndMut {
|
||||
mutbl: hir::Mutability::Not,
|
||||
ty: array_ty,
|
||||
}) => match array_ty.kind() {
|
||||
ty::Array(ty_elem, _) => Some(ty_elem),
|
||||
_ => None,
|
||||
},
|
||||
let opt_ty_elem_mut = match ty_from.kind() {
|
||||
ty::RawPtr(ty::TypeAndMut { mutbl: array_mut, ty: array_ty }) => {
|
||||
match array_ty.kind() {
|
||||
ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let ty_elem = match opt_ty_elem {
|
||||
Some(ty_elem) => ty_elem,
|
||||
let (ty_elem, ty_mut) = match opt_ty_elem_mut {
|
||||
Some(ty_elem_mut) => ty_elem_mut,
|
||||
None => {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2215,11 +2214,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let ty_to = match ty.kind() {
|
||||
ty::RawPtr(ty::TypeAndMut {
|
||||
mutbl: hir::Mutability::Not,
|
||||
ty: ty_to,
|
||||
}) => ty_to,
|
||||
let (ty_to, ty_to_mut) = match ty.kind() {
|
||||
ty::RawPtr(ty::TypeAndMut { mutbl: ty_to_mut, ty: ty_to }) => {
|
||||
(ty_to, *ty_to_mut)
|
||||
}
|
||||
_ => {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2231,6 +2229,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
if ty_to_mut == Mutability::Mut && ty_mut == Mutability::Not {
|
||||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
"ArrayToPointer cast from const {:?} to mut {:?}",
|
||||
ty,
|
||||
ty_to
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(terr) = self.sub_types(
|
||||
ty_elem,
|
||||
ty_to,
|
||||
|
@ -43,6 +43,7 @@ bitflags! {
|
||||
const LEAK = 1 << 1;
|
||||
const MEMORY = 1 << 2;
|
||||
const THREAD = 1 << 3;
|
||||
const HWADDRESS = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +57,7 @@ impl fmt::Display for SanitizerSet {
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::THREAD => "thread",
|
||||
SanitizerSet::HWADDRESS => "hwaddress",
|
||||
_ => panic!("unrecognized sanitizer {:?}", s),
|
||||
};
|
||||
if !first {
|
||||
@ -73,12 +75,18 @@ impl IntoIterator for SanitizerSet {
|
||||
type IntoIter = std::vec::IntoIter<SanitizerSet>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
[SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD]
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&s| self.contains(s))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
[
|
||||
SanitizerSet::ADDRESS,
|
||||
SanitizerSet::LEAK,
|
||||
SanitizerSet::MEMORY,
|
||||
SanitizerSet::THREAD,
|
||||
SanitizerSet::HWADDRESS,
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&s| self.contains(s))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ macro_rules! options {
|
||||
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
|
||||
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
|
||||
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `hwaddress`, `leak`, `memory` or `thread`";
|
||||
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
|
||||
pub const parse_cfguard: &str =
|
||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
||||
@ -476,6 +476,7 @@ macro_rules! options {
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"thread" => SanitizerSet::THREAD,
|
||||
"hwaddress" => SanitizerSet::HWADDRESS,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
@ -1126,7 +1126,8 @@ impl Session {
|
||||
self.opts.optimize != config::OptLevel::No
|
||||
// AddressSanitizer uses lifetimes to detect use after scope bugs.
|
||||
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|
||||
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
|
||||
// HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
|
||||
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
|
||||
}
|
||||
|
||||
pub fn link_dead_code(&self) -> bool {
|
||||
@ -1562,6 +1563,8 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
||||
"x86_64-unknown-freebsd",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
];
|
||||
const HWASAN_SUPPORTED_TARGETS: &[&str] =
|
||||
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
|
||||
|
||||
// Sanitizers can only be used on some tested platforms.
|
||||
for s in sess.opts.debugging_opts.sanitizer {
|
||||
@ -1570,6 +1573,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
||||
SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS,
|
||||
SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS,
|
||||
SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS,
|
||||
SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS,
|
||||
_ => panic!("unrecognized sanitizer {}", s),
|
||||
};
|
||||
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
|
||||
|
@ -593,6 +593,7 @@ symbols! {
|
||||
html_no_source,
|
||||
html_playground_url,
|
||||
html_root_url,
|
||||
hwaddress,
|
||||
i,
|
||||
i128,
|
||||
i128_type,
|
||||
|
@ -765,9 +765,8 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||
m_expr: ty::TypeAndMut<'tcx>,
|
||||
m_cast: ty::TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
// array-ptr-cast.
|
||||
|
||||
if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not {
|
||||
// array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const
|
||||
if m_expr.mutbl == hir::Mutability::Mut || m_cast.mutbl == hir::Mutability::Not {
|
||||
if let ty::Array(ety, _) = m_expr.ty.kind() {
|
||||
// Due to the limitations of LLVM global constants,
|
||||
// region pointers end up pointing at copies of
|
||||
|
@ -1211,7 +1211,7 @@ fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
|
||||
ProjectionKind::Subslice => String::from("Subslice"),
|
||||
};
|
||||
if i != 0 {
|
||||
projections_str.push_str(",");
|
||||
projections_str.push(',');
|
||||
}
|
||||
projections_str.push_str(proj.as_str());
|
||||
}
|
||||
@ -1382,14 +1382,8 @@ fn determine_place_ancestry_relation(
|
||||
// Assume of length of projections_b = m
|
||||
let projections_b = &place_b.projections;
|
||||
|
||||
let mut same_initial_projections = true;
|
||||
|
||||
for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) {
|
||||
if proj_a != proj_b {
|
||||
same_initial_projections = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let same_initial_projections =
|
||||
projections_a.iter().zip(projections_b.iter()).all(|(proj_a, proj_b)| proj_a == proj_b);
|
||||
|
||||
if same_initial_projections {
|
||||
// First min(n, m) projections are the same
|
||||
|
@ -2709,10 +2709,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||
} else if item.has_name(sym::thread) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
|
||||
} else if item.has_name(sym::hwaddress) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
|
||||
} else {
|
||||
tcx.sess
|
||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
||||
.note("expected one of: `address`, `memory` or `thread`")
|
||||
.note("expected one of: `address`, `hwaddress`, `memory` or `thread`")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,73 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
|
||||
let parent_node = tcx.hir().get(parent_node_id);
|
||||
|
||||
match parent_node {
|
||||
// This match arm is for when the def_id appears in a GAT whose
|
||||
// path can't be resolved without typechecking e.g.
|
||||
//
|
||||
// trait Foo {
|
||||
// type Assoc<const N: usize>;
|
||||
// fn foo() -> Self::Assoc<3>;
|
||||
// }
|
||||
//
|
||||
// In the above code we would call this query with the def_id of 3 and
|
||||
// the parent_node we match on would be the hir node for Self::Assoc<3>
|
||||
//
|
||||
// `Self::Assoc<3>` cant be resolved without typchecking here as we
|
||||
// didnt write <Self as Foo>::Assoc<3>. If we did then another match
|
||||
// arm would handle this.
|
||||
//
|
||||
// I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU
|
||||
Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
|
||||
// Find the Item containing the associated type so we can create an ItemCtxt.
|
||||
// Using the ItemCtxt convert the HIR for the unresolved assoc type into a
|
||||
// ty which is a fully resolved projection.
|
||||
// For the code example above, this would mean converting Self::Assoc<3>
|
||||
// into a ty::Projection(<Self as Foo>::Assoc<3>)
|
||||
let item_hir_id = tcx
|
||||
.hir()
|
||||
.parent_iter(hir_id)
|
||||
.filter(|(_, node)| matches!(node, Node::Item(_)))
|
||||
.map(|(id, _)| id)
|
||||
.next()
|
||||
.unwrap();
|
||||
let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id();
|
||||
let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>;
|
||||
let ty = item_ctxt.ast_ty_to_ty(hir_ty);
|
||||
|
||||
// Iterate through the generics of the projection to find the one that corresponds to
|
||||
// the def_id that this query was called with. We filter to only const args here as a
|
||||
// precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
|
||||
// but it can't hurt to be safe ^^
|
||||
if let ty::Projection(projection) = ty.kind() {
|
||||
let generics = tcx.generics_of(projection.item_def_id);
|
||||
|
||||
let arg_index = segment
|
||||
.args
|
||||
.and_then(|args| {
|
||||
args.args
|
||||
.iter()
|
||||
.filter(|arg| arg.is_const())
|
||||
.position(|arg| arg.id() == hir_id)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
bug!("no arg matching AnonConst in segment");
|
||||
});
|
||||
|
||||
return generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const))
|
||||
.nth(arg_index)
|
||||
.map(|param| param.def_id);
|
||||
}
|
||||
|
||||
// I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU
|
||||
tcx.sess.delay_span_bug(
|
||||
tcx.def_span(def_id),
|
||||
"unexpected non-GAT usage of an anon const",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Node::Expr(&Expr {
|
||||
kind:
|
||||
ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)),
|
||||
|
@ -56,6 +56,7 @@ This API is completely unstable and subject to change.
|
||||
*/
|
||||
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(bindings_after_at)]
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
|
@ -99,13 +99,11 @@
|
||||
//! pub struct Foo;
|
||||
//!
|
||||
//! #[no_mangle]
|
||||
//! #[allow(improper_ctypes_definitions)]
|
||||
//! pub extern "C" fn foo_new() -> Box<Foo> {
|
||||
//! Box::new(Foo)
|
||||
//! }
|
||||
//!
|
||||
//! #[no_mangle]
|
||||
//! #[allow(improper_ctypes_definitions)]
|
||||
//! pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}
|
||||
//! ```
|
||||
//!
|
||||
|
@ -198,12 +198,13 @@ where
|
||||
Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))
|
||||
}
|
||||
} else if A::may_have_side_effect() && self.index < self.a.size() {
|
||||
// match the base implementation's potential side effects
|
||||
// SAFETY: we just checked that `self.index` < `self.a.len()`
|
||||
unsafe {
|
||||
self.a.__iterator_get_unchecked(self.index);
|
||||
}
|
||||
let i = self.index;
|
||||
self.index += 1;
|
||||
// match the base implementation's potential side effects
|
||||
// SAFETY: we just checked that `i` < `self.a.len()`
|
||||
unsafe {
|
||||
self.a.__iterator_get_unchecked(i);
|
||||
}
|
||||
None
|
||||
} else {
|
||||
None
|
||||
|
@ -2082,6 +2082,12 @@ impl<T> [T] {
|
||||
/// [`Result::Err`] is returned, containing the index where a matching
|
||||
/// element could be inserted while maintaining sorted order.
|
||||
///
|
||||
/// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
|
||||
///
|
||||
/// [`binary_search_by`]: #method.binary_search_by
|
||||
/// [`binary_search_by_key`]: #method.binary_search_by_key
|
||||
/// [`partition_point`]: #method.partition_point
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Looks up a series of four elements. The first is found, with a
|
||||
@ -2129,6 +2135,12 @@ impl<T> [T] {
|
||||
/// [`Result::Err`] is returned, containing the index where a matching
|
||||
/// element could be inserted while maintaining sorted order.
|
||||
///
|
||||
/// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
|
||||
///
|
||||
/// [`binary_search`]: #method.binary_search
|
||||
/// [`binary_search_by_key`]: #method.binary_search_by_key
|
||||
/// [`partition_point`]: #method.partition_point
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Looks up a series of four elements. The first is found, with a
|
||||
@ -2186,7 +2198,12 @@ impl<T> [T] {
|
||||
/// [`Result::Err`] is returned, containing the index where a matching
|
||||
/// element could be inserted while maintaining sorted order.
|
||||
///
|
||||
/// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
|
||||
///
|
||||
/// [`sort_by_key`]: #method.sort_by_key
|
||||
/// [`binary_search`]: #method.binary_search
|
||||
/// [`binary_search_by`]: #method.binary_search_by
|
||||
/// [`partition_point`]: #method.partition_point
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -3399,11 +3416,15 @@ impl<T> [T] {
|
||||
/// If this slice is not partitioned, the returned result is unspecified and meaningless,
|
||||
/// as this method performs a kind of binary search.
|
||||
///
|
||||
/// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`].
|
||||
///
|
||||
/// [`binary_search`]: #method.binary_search
|
||||
/// [`binary_search_by`]: #method.binary_search_by
|
||||
/// [`binary_search_by_key`]: #method.binary_search_by_key
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(partition_point)]
|
||||
///
|
||||
/// let v = [1, 2, 3, 3, 5, 6, 7];
|
||||
/// let i = v.partition_point(|&x| x < 5);
|
||||
///
|
||||
@ -3411,7 +3432,7 @@ impl<T> [T] {
|
||||
/// assert!(v[..i].iter().all(|&x| x < 5));
|
||||
/// assert!(v[i..].iter().all(|&x| !(x < 5)));
|
||||
/// ```
|
||||
#[unstable(feature = "partition_point", reason = "new API", issue = "73831")]
|
||||
#[stable(feature = "partition_point", since = "1.52.0")]
|
||||
pub fn partition_point<P>(&self, mut pred: P) -> usize
|
||||
where
|
||||
P: FnMut(&T) -> bool,
|
||||
|
@ -67,7 +67,6 @@
|
||||
#![feature(option_result_unwrap_unchecked)]
|
||||
#![feature(option_unwrap_none)]
|
||||
#![feature(peekable_peek_mut)]
|
||||
#![feature(partition_point)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(unsafe_block_in_unsafe_fn)]
|
||||
#![feature(int_bits_const)]
|
||||
|
@ -22,6 +22,7 @@ use crate::str;
|
||||
use crate::sys::cvt;
|
||||
use crate::sys::fd;
|
||||
use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
|
||||
use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock};
|
||||
use crate::vec;
|
||||
|
||||
use libc::{c_char, c_int, c_void};
|
||||
@ -490,20 +491,20 @@ pub unsafe fn environ() -> *mut *const *const c_char {
|
||||
extern "C" {
|
||||
static mut environ: *const *const c_char;
|
||||
}
|
||||
&mut environ
|
||||
ptr::addr_of_mut!(environ)
|
||||
}
|
||||
|
||||
pub unsafe fn env_lock() -> StaticMutexGuard {
|
||||
// It is UB to attempt to acquire this mutex reentrantly!
|
||||
static ENV_LOCK: StaticMutex = StaticMutex::new();
|
||||
ENV_LOCK.lock()
|
||||
static ENV_LOCK: StaticRWLock = StaticRWLock::new();
|
||||
|
||||
pub fn env_read_lock() -> RWLockReadGuard {
|
||||
ENV_LOCK.read_with_guard()
|
||||
}
|
||||
|
||||
/// Returns a vector of (variable, value) byte-vector pairs for all the
|
||||
/// environment variables of the current process.
|
||||
pub fn env() -> Env {
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
let _guard = env_read_lock();
|
||||
let mut environ = *environ();
|
||||
let mut result = Vec::new();
|
||||
if !environ.is_null() {
|
||||
@ -540,7 +541,7 @@ pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
|
||||
// always None as well
|
||||
let k = CString::new(k.as_bytes())?;
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
let _guard = env_read_lock();
|
||||
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
|
||||
let ret = if s.is_null() {
|
||||
None
|
||||
@ -556,7 +557,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
||||
let v = CString::new(v.as_bytes())?;
|
||||
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
let _guard = ENV_LOCK.write_with_guard();
|
||||
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
|
||||
}
|
||||
}
|
||||
@ -565,7 +566,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
||||
let nbuf = CString::new(n.as_bytes())?;
|
||||
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
let _guard = ENV_LOCK.write_with_guard();
|
||||
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ impl Command {
|
||||
// a lock any more because the parent won't do anything and the child is
|
||||
// in its own process.
|
||||
let result = unsafe {
|
||||
let _env_lock = sys::os::env_lock();
|
||||
let _env_lock = sys::os::env_read_lock();
|
||||
cvt(libc::fork())?
|
||||
};
|
||||
|
||||
@ -124,7 +124,7 @@ impl Command {
|
||||
// Similar to when forking, we want to ensure that access to
|
||||
// the environment is synchronized, so make sure to grab the
|
||||
// environment lock before we try to exec.
|
||||
let _lock = sys::os::env_lock();
|
||||
let _lock = sys::os::env_read_lock();
|
||||
|
||||
let Err(e) = self.do_exec(theirs, envp.as_ref());
|
||||
e
|
||||
@ -404,7 +404,7 @@ impl Command {
|
||||
cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
|
||||
|
||||
// Make sure we synchronize access to the global `environ` resource
|
||||
let _env_lock = sys::os::env_lock();
|
||||
let _env_lock = sys::os::env_read_lock();
|
||||
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
|
||||
cvt_nz(libc::posix_spawnp(
|
||||
&mut p.pid,
|
||||
|
@ -86,3 +86,62 @@ impl RWLock {
|
||||
self.0.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
// the cfg annotations only exist due to dead code warnings. the code itself is portable
|
||||
#[cfg(unix)]
|
||||
pub struct StaticRWLock(RWLock);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl StaticRWLock {
|
||||
pub const fn new() -> StaticRWLock {
|
||||
StaticRWLock(RWLock::new())
|
||||
}
|
||||
|
||||
/// Acquires shared access to the underlying lock, blocking the current
|
||||
/// thread to do so.
|
||||
///
|
||||
/// The lock is automatically unlocked when the returned guard is dropped.
|
||||
#[inline]
|
||||
pub fn read_with_guard(&'static self) -> RWLockReadGuard {
|
||||
// Safety: All methods require static references, therefore self
|
||||
// cannot be moved between invocations.
|
||||
unsafe {
|
||||
self.0.read();
|
||||
}
|
||||
RWLockReadGuard(&self.0)
|
||||
}
|
||||
|
||||
/// Acquires write access to the underlying lock, blocking the current thread
|
||||
/// to do so.
|
||||
///
|
||||
/// The lock is automatically unlocked when the returned guard is dropped.
|
||||
#[inline]
|
||||
pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
|
||||
// Safety: All methods require static references, therefore self
|
||||
// cannot be moved between invocations.
|
||||
unsafe {
|
||||
self.0.write();
|
||||
}
|
||||
RWLockWriteGuard(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub struct RWLockReadGuard(&'static RWLock);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl Drop for RWLockReadGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.read_unlock() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub struct RWLockWriteGuard(&'static RWLock);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl Drop for RWLockWriteGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.write_unlock() }
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ o("option-checking", None, "complain about unrecognized options in this configur
|
||||
o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
|
||||
o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
|
||||
o("vendor", "build.vendor", "enable usage of vendored Rust crates")
|
||||
o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)")
|
||||
o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)")
|
||||
o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
|
||||
o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
|
||||
o("profiler", "build.profiler", "build the profiler runtime")
|
||||
|
@ -804,7 +804,7 @@ fn supported_sanitizers(
|
||||
"aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
|
||||
"aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
|
||||
"aarch64-unknown-linux-gnu" => {
|
||||
common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"])
|
||||
common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
|
||||
}
|
||||
"x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
|
||||
"x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
|
||||
|
@ -7,12 +7,15 @@ The tracking issue for this feature is: [#39699](https://github.com/rust-lang/ru
|
||||
This feature allows for use of one of following sanitizers:
|
||||
|
||||
* [AddressSanitizer][clang-asan] a fast memory error detector.
|
||||
* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to
|
||||
AddressSanitizer, but based on partial hardware assistance.
|
||||
* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
|
||||
* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
|
||||
* [ThreadSanitizer][clang-tsan] a fast data race detector.
|
||||
|
||||
To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`,
|
||||
`-Zsanitizer=memory` or `-Zsanitizer=thread`.
|
||||
To enable a sanitizer compile with `-Zsanitizer=address`,
|
||||
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or
|
||||
`-Zsanitizer=thread`.
|
||||
|
||||
# AddressSanitizer
|
||||
|
||||
@ -174,6 +177,86 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
==39249==ABORTING
|
||||
```
|
||||
|
||||
# HWAddressSanitizer
|
||||
|
||||
HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much
|
||||
less memory.
|
||||
|
||||
HWAddressSanitizer is supported on the following targets:
|
||||
|
||||
* `aarch64-linux-android`
|
||||
* `aarch64-unknown-linux-gnu`
|
||||
|
||||
HWAddressSanitizer requires `tagged-globals` target feature to instrument
|
||||
globals. To enable this target feature compile with `-C
|
||||
target-feature=+tagged-globals`
|
||||
|
||||
## Example
|
||||
|
||||
Heap buffer overflow:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let xs = vec![0, 1, 2, 3];
|
||||
let _y = unsafe { *xs.as_ptr().offset(4) };
|
||||
}
|
||||
```
|
||||
|
||||
```shell
|
||||
$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C
|
||||
linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target
|
||||
aarch64-unknown-linux-gnu
|
||||
```
|
||||
|
||||
```shell
|
||||
$ ./main
|
||||
==241==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaae0ae4a98
|
||||
READ of size 4 at 0xefdeffff0050 tags: 2c/00 (ptr/mem) in thread T0
|
||||
#0 0xaaaae0ae4a94 (/.../main+0x54a94)
|
||||
...
|
||||
|
||||
[0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16
|
||||
0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050)
|
||||
allocated here:
|
||||
#0 0xaaaae0acb80c (/.../main+0x3b80c)
|
||||
...
|
||||
|
||||
Thread: T0 0xeffe00002000 stack: [0xffffc28ad000,0xffffc30ad000) sz: 8388608 tls: [0xffffaa10a020,0xffffaa10a7d0)
|
||||
Memory tags around the buggy address (one tag corresponds to 16 bytes):
|
||||
0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0xfefceffff000: d7 d7 05 00 2c [00] 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
|
||||
0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
|
||||
=>0xfefceffff000: .. .. 8c .. .. [..] .. .. .. .. .. .. .. .. .. ..
|
||||
0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
|
||||
See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
|
||||
Registers where the failure occurred (pc 0xaaaae0ae4a98):
|
||||
x0 2c00efdeffff0050 x1 0000000000000004 x2 0000000000000004 x3 0000000000000000
|
||||
x4 0000fffefc30ac37 x5 000000000000005d x6 00000ffffc30ac37 x7 0000efff00000000
|
||||
x8 2c00efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000
|
||||
x12 0200effe00000310 x13 0200effe00000310 x14 0000000000000008 x15 5d00ffffc30ac360
|
||||
x16 0000aaaae0ad062c x17 0000000000000003 x18 0000000000000001 x19 0000ffffc30ac658
|
||||
x20 4e00ffffc30ac6e0 x21 0000aaaae0ac5e10 x22 0000000000000000 x23 0000000000000000
|
||||
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
|
||||
x28 0000000000000000 x29 0000ffffc30ac5a0 x30 0000aaaae0ae4a98
|
||||
SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94)
|
||||
```
|
||||
|
||||
# LeakSanitizer
|
||||
|
||||
LeakSanitizer is run-time memory leak detector.
|
||||
@ -321,11 +404,13 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
|
||||
|
||||
* [Sanitizers project page](https://github.com/google/sanitizers/wiki/)
|
||||
* [AddressSanitizer in Clang][clang-asan]
|
||||
* [HWAddressSanitizer in Clang][clang-hwasan]
|
||||
* [LeakSanitizer in Clang][clang-lsan]
|
||||
* [MemorySanitizer in Clang][clang-msan]
|
||||
* [ThreadSanitizer in Clang][clang-tsan]
|
||||
|
||||
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
|
||||
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
|
||||
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
|
||||
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
|
||||
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
|
||||
|
@ -546,9 +546,12 @@ crate fn make_test(
|
||||
// compiler.
|
||||
if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") {
|
||||
if let Some(cratename) = cratename {
|
||||
// Make sure its actually used if not included.
|
||||
// Don't inject `extern crate` if the crate is never used.
|
||||
// NOTE: this is terribly inaccurate because it doesn't actually
|
||||
// parse the source, but only has false positives, not false
|
||||
// negatives.
|
||||
if s.contains(cratename) {
|
||||
prog.push_str(&format!("extern crate {};\n", cratename));
|
||||
prog.push_str(&format!("extern crate r#{};\n", cratename));
|
||||
line_offset += 1;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ fn make_test_crate_name() {
|
||||
let input = "use asdf::qwop;
|
||||
assert_eq!(2+2, 4);";
|
||||
let expected = "#![allow(unused)]
|
||||
extern crate asdf;
|
||||
extern crate r#asdf;
|
||||
fn main() {
|
||||
use asdf::qwop;
|
||||
assert_eq!(2+2, 4);
|
||||
@ -128,7 +128,7 @@ fn make_test_opts_attrs() {
|
||||
let input = "use asdf::qwop;
|
||||
assert_eq!(2+2, 4);";
|
||||
let expected = "#![feature(sick_rad)]
|
||||
extern crate asdf;
|
||||
extern crate r#asdf;
|
||||
fn main() {
|
||||
use asdf::qwop;
|
||||
assert_eq!(2+2, 4);
|
||||
@ -141,7 +141,7 @@ assert_eq!(2+2, 4);
|
||||
opts.attrs.push("feature(hella_dope)".to_string());
|
||||
let expected = "#![feature(sick_rad)]
|
||||
#![feature(hella_dope)]
|
||||
extern crate asdf;
|
||||
extern crate r#asdf;
|
||||
fn main() {
|
||||
use asdf::qwop;
|
||||
assert_eq!(2+2, 4);
|
||||
@ -250,7 +250,7 @@ assert_eq!(asdf::foo, 4);";
|
||||
|
||||
let expected = "#![allow(unused)]
|
||||
extern crate hella_qwop;
|
||||
extern crate asdf;
|
||||
extern crate r#asdf;
|
||||
fn main() {
|
||||
assert_eq!(asdf::foo, 4);
|
||||
}"
|
||||
|
@ -11,4 +11,4 @@
|
||||
pub fn dummy() {}
|
||||
|
||||
// ensure that `extern crate foo;` was inserted into code snips automatically:
|
||||
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run"
|
||||
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20r%23foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run"
|
||||
|
@ -1,7 +1,11 @@
|
||||
// Issue #14893. Tests that casts from vectors don't behave strangely in the
|
||||
// presence of the `_` type shorthand notation.
|
||||
//
|
||||
// Update: after a change to the way casts are done, we have more type information
|
||||
// around and so the errors here are no longer exactly the same.
|
||||
//
|
||||
// Update: With PR #81479 some of the previously rejected cases are now allowed.
|
||||
// New test cases added.
|
||||
|
||||
struct X {
|
||||
y: [u8; 2],
|
||||
@ -12,13 +16,19 @@ fn main() {
|
||||
|
||||
// No longer a type mismatch - the `_` can be fully resolved by type inference.
|
||||
let p1: *const u8 = &x1.y as *const _;
|
||||
let p1: *mut u8 = &x1.y as *mut _;
|
||||
//~^ ERROR: casting `&[u8; 2]` as `*mut u8` is invalid
|
||||
let t1: *const [u8; 2] = &x1.y as *const _;
|
||||
let t1: *mut [u8; 2] = &x1.y as *mut _;
|
||||
//~^ ERROR: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid
|
||||
let h1: *const [u8; 2] = &x1.y as *const [u8; 2];
|
||||
let t1: *mut [u8; 2] = &x1.y as *mut [u8; 2];
|
||||
//~^ ERROR: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid
|
||||
|
||||
let mut x1 = X { y: [0, 0] };
|
||||
|
||||
// This is still an error since we don't allow casts from &mut [T; n] to *mut T.
|
||||
let p1: *mut u8 = &mut x1.y as *mut _; //~ ERROR casting
|
||||
let p1: *mut u8 = &mut x1.y as *mut _;
|
||||
let p2: *const u8 = &mut x1.y as *const _;
|
||||
let t1: *mut [u8; 2] = &mut x1.y as *mut _;
|
||||
let h1: *mut [u8; 2] = &mut x1.y as *mut [u8; 2];
|
||||
}
|
||||
|
@ -1,9 +1,21 @@
|
||||
error[E0606]: casting `&mut [u8; 2]` as `*mut u8` is invalid
|
||||
--> $DIR/vector-cast-weirdness.rs:21:23
|
||||
error[E0606]: casting `&[u8; 2]` as `*mut u8` is invalid
|
||||
--> $DIR/vector-cast-weirdness.rs:19:23
|
||||
|
|
||||
LL | let p1: *mut u8 = &mut x1.y as *mut _;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
LL | let p1: *mut u8 = &x1.y as *mut _;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0606]: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid
|
||||
--> $DIR/vector-cast-weirdness.rs:22:28
|
||||
|
|
||||
LL | let t1: *mut [u8; 2] = &x1.y as *mut _;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0606]: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid
|
||||
--> $DIR/vector-cast-weirdness.rs:25:28
|
||||
|
|
||||
LL | let t1: *mut [u8; 2] = &x1.y as *mut [u8; 2];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0606`.
|
||||
|
@ -0,0 +1,22 @@
|
||||
// run-pass
|
||||
#![feature(generic_associated_types)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// This test unsures that with_opt_const_param returns the
|
||||
// def_id of the N param in the Foo::Assoc GAT.
|
||||
|
||||
trait Foo {
|
||||
type Assoc<const N: usize>;
|
||||
fn foo(&self) -> Self::Assoc<3>;
|
||||
}
|
||||
|
||||
impl Foo for () {
|
||||
type Assoc<const N: usize> = [(); N];
|
||||
fn foo(&self) -> Self::Assoc<3> {
|
||||
[(); 3]
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(().foo(), [(); 3]);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// run-pass
|
||||
#![feature(generic_associated_types)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// This test unsures that with_opt_const_param returns the
|
||||
// def_id of the N param in the Foo::Assoc GAT.
|
||||
|
||||
trait Foo {
|
||||
type Assoc<const N: usize>;
|
||||
fn foo<const N: usize>(&self) -> Self::Assoc<N>;
|
||||
}
|
||||
|
||||
impl Foo for () {
|
||||
type Assoc<const N: usize> = [(); N];
|
||||
fn foo<const N: usize>(&self) -> Self::Assoc<N> {
|
||||
[(); N]
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(().foo::<10>(), [(); 10]);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// run-pass
|
||||
#![feature(generic_associated_types)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// This test unsures that with_opt_const_param returns the
|
||||
// def_id of the N param in the Bar::Assoc GAT.
|
||||
|
||||
trait Bar {
|
||||
type Assoc<const N: usize>;
|
||||
}
|
||||
trait Foo: Bar {
|
||||
fn foo(&self) -> Self::Assoc<3>;
|
||||
}
|
||||
|
||||
impl Bar for () {
|
||||
type Assoc<const N: usize> = [(); N];
|
||||
}
|
||||
|
||||
impl Foo for () {
|
||||
fn foo(&self) -> Self::Assoc<3> {
|
||||
[(); 3]
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(().foo(), [(); 3]);
|
||||
}
|
@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize`
|
||||
LL | #[no_sanitize(brontosaurus)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected one of: `address`, `memory` or `thread`
|
||||
= note: expected one of: `address`, `hwaddress`, `memory` or `thread`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
19
src/test/ui/sanitize/hwaddress.rs
Normal file
19
src/test/ui/sanitize/hwaddress.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// needs-sanitizer-support
|
||||
// needs-sanitizer-hwaddress
|
||||
//
|
||||
// compile-flags: -Z sanitizer=hwaddress -O -g
|
||||
//
|
||||
// run-fail
|
||||
// error-pattern: HWAddressSanitizer: tag-mismatch
|
||||
|
||||
#![feature(test)]
|
||||
|
||||
use std::hint::black_box;
|
||||
|
||||
fn main() {
|
||||
let xs = vec![0, 1, 2, 3];
|
||||
// Avoid optimizing everything out.
|
||||
let xs = black_box(xs.as_ptr());
|
||||
let code = unsafe { *xs.offset(4) };
|
||||
std::process::exit(code);
|
||||
}
|
@ -48,6 +48,7 @@ impl EarlyProps {
|
||||
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
|
||||
iter_header(testfile, None, rdr, &mut |ln| {
|
||||
// we should check if any only-<platform> exists and if it exists
|
||||
@ -101,6 +102,10 @@ impl EarlyProps {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress") {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
@ -110,6 +110,9 @@ pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[
|
||||
"x86_64-unknown-linux-gnu",
|
||||
];
|
||||
|
||||
pub const HWASAN_SUPPORTED_TARGETS: &[&str] =
|
||||
&["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
|
||||
|
||||
const BIG_ENDIAN: &[&str] = &[
|
||||
"aarch64_be",
|
||||
"armebv7r",
|
||||
|
Loading…
Reference in New Issue
Block a user