mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
Auto merge of #154384 - JonathanBrouwer:rollup-Wou9g2B, r=JonathanBrouwer
Rollup of 22 pull requests Successful merges: - rust-lang/rust#153049 (Add `-Zsanitize=kernel-hwaddress`) - rust-lang/rust#153702 (Add macro matcher for `guard` fragment specifier) - rust-lang/rust#154200 (debuginfo: emit DW_TAG_call_site entries) - rust-lang/rust#154263 (interpret: when passing an argument fails, point at that argument) - rust-lang/rust#154269 (miri recursive validation: only check one layer deep) - rust-lang/rust#154313 (Init `self_decl` with a correct visibility) - rust-lang/rust#154344 (Update LLVM to 22.1.2) - rust-lang/rust#154348 (re-enable enzyme/autodiff builds on dist-aarch64-apple) - rust-lang/rust#154351 (Overhaul `Erasable` impls) - rust-lang/rust#154363 (delegation: fix zero-args nested delegation ICE) - rust-lang/rust#154364 (delegation: don't propagate synthetic params, remove lifetime hacks) - rust-lang/rust#151148 (Add functions to `GrowableBitSet`) - rust-lang/rust#154090 (Move tests in the statics category) - rust-lang/rust#154112 (some `tests/ui/macros` cleanup) - rust-lang/rust#154131 (begin `tests/ui/structs-enums` cleanup) - rust-lang/rust#154216 (unstably mark `NonNull::with_exposed_provenance` as const) - rust-lang/rust#154230 (Moved and rename issue-50411 to tests/ui/mir/inliner-double-elaborate) - rust-lang/rust#154233 (Move ui/issues tests to relevant subdirectories) - rust-lang/rust#154288 (Fix typo in doc comment for `char::to_titlecase`) - rust-lang/rust#154355 (delegation: add const type ICE test) - rust-lang/rust#154358 (install-template.sh: Optimize by using Bourne shell builtins.) - rust-lang/rust#154360 (fromrangeiter-overflow-checks: accept optional `signext` for argument)
This commit is contained in:
@@ -938,7 +938,7 @@ pub enum PatKind {
|
||||
Never,
|
||||
|
||||
/// A guard pattern (e.g., `x if guard(x)`).
|
||||
Guard(Box<Pat>, Box<Expr>),
|
||||
Guard(Box<Pat>, Box<Guard>),
|
||||
|
||||
/// Parentheses in patterns used for grouping (i.e., `(PAT)`).
|
||||
Paren(Box<Pat>),
|
||||
@@ -1346,7 +1346,7 @@ pub struct Arm {
|
||||
/// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`.
|
||||
pub pat: Box<Pat>,
|
||||
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`.
|
||||
pub guard: Option<Box<Expr>>,
|
||||
pub guard: Option<Box<Guard>>,
|
||||
/// Match arm body. Omitted if the pattern is a never pattern.
|
||||
pub body: Option<Box<Expr>>,
|
||||
pub span: Span,
|
||||
@@ -3954,6 +3954,18 @@ impl ConstBlockItem {
|
||||
pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP };
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct Guard {
|
||||
pub cond: Expr,
|
||||
pub span_with_leading_if: Span,
|
||||
}
|
||||
|
||||
impl Guard {
|
||||
pub fn span(&self) -> Span {
|
||||
self.cond.span
|
||||
}
|
||||
}
|
||||
|
||||
// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub enum ItemKind {
|
||||
|
||||
@@ -94,6 +94,7 @@ pub enum MetaVarKind {
|
||||
},
|
||||
Path,
|
||||
Vis,
|
||||
Guard,
|
||||
TT,
|
||||
}
|
||||
|
||||
@@ -114,6 +115,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
MetaVarKind::Meta { .. } => sym::meta,
|
||||
MetaVarKind::Path => sym::path,
|
||||
MetaVarKind::Vis => sym::vis,
|
||||
MetaVarKind::Guard => sym::guard,
|
||||
MetaVarKind::TT => sym::tt,
|
||||
};
|
||||
write!(f, "{sym}")
|
||||
@@ -1124,6 +1126,7 @@ pub enum NonterminalKind {
|
||||
Meta,
|
||||
Path,
|
||||
Vis,
|
||||
Guard,
|
||||
TT,
|
||||
}
|
||||
|
||||
@@ -1161,6 +1164,7 @@ pub fn from_symbol(
|
||||
sym::meta => NonterminalKind::Meta,
|
||||
sym::path => NonterminalKind::Path,
|
||||
sym::vis => NonterminalKind::Vis,
|
||||
sym::guard => NonterminalKind::Guard,
|
||||
sym::tt => NonterminalKind::TT,
|
||||
_ => return None,
|
||||
})
|
||||
@@ -1182,6 +1186,7 @@ fn symbol(self) -> Symbol {
|
||||
NonterminalKind::Meta => sym::meta,
|
||||
NonterminalKind::Path => sym::path,
|
||||
NonterminalKind::Vis => sym::vis,
|
||||
NonterminalKind::Guard => sym::guard,
|
||||
NonterminalKind::TT => sym::tt,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +442,7 @@ pub fn ctxt(&self) -> Option<FnCtxt> {
|
||||
FormatArguments,
|
||||
FormatPlaceholder,
|
||||
GenericParamKind,
|
||||
Guard,
|
||||
Impl,
|
||||
ImplPolarity,
|
||||
Inline,
|
||||
|
||||
@@ -432,6 +432,17 @@ fn lower_delegation_body(
|
||||
args.push(arg);
|
||||
}
|
||||
|
||||
// If we have no params in signature function but user still wrote some code in
|
||||
// delegation body, then add this code as first arg, eventually an error will be shown,
|
||||
// also nested delegations may need to access information about this code (#154332),
|
||||
// so it is better to leave this code as opposed to bodies of extern functions,
|
||||
// which are completely erased from existence.
|
||||
if param_count == 0
|
||||
&& let Some(block) = block
|
||||
{
|
||||
args.push(this.lower_target_expr(&block));
|
||||
}
|
||||
|
||||
let final_expr = this.finalize_body_lowering(delegation, args, generics, span);
|
||||
|
||||
(this.arena.alloc_from_iter(parameters), final_expr)
|
||||
|
||||
@@ -337,7 +337,6 @@ fn uplift_delegation_generic_params(
|
||||
// HACK: for now we generate predicates such that all lifetimes are early bound,
|
||||
// we can not not generate early-bound lifetimes, but we can't know which of them
|
||||
// are late-bound at this level of compilation.
|
||||
// FIXME(fn_delegation): proper support for late bound lifetimes.
|
||||
let predicates =
|
||||
self.arena.alloc_from_iter(params.iter().filter_map(|p| {
|
||||
p.is_lifetime().then(|| self.generate_lifetime_predicate(p, span))
|
||||
@@ -391,7 +390,7 @@ fn create_generics_args_from_params(
|
||||
self.arena.alloc(hir::GenericArgs {
|
||||
args: self.arena.alloc_from_iter(params.iter().filter_map(|p| {
|
||||
// Skip self generic arg, we do not need to propagate it.
|
||||
if p.name.ident().name == kw::SelfUpper {
|
||||
if p.name.ident().name == kw::SelfUpper || p.is_impl_trait() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -640,7 +640,7 @@ fn wrap_in_try_constructor(
|
||||
|
||||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||
let pat = self.lower_pat(&arm.pat);
|
||||
let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
|
||||
let guard = arm.guard.as_ref().map(|guard| self.lower_expr(&guard.cond));
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs, arm.span, Target::Arm);
|
||||
@@ -663,7 +663,7 @@ fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||
} else if let Some(body) = &arm.body {
|
||||
self.dcx().emit_err(NeverPatternWithBody { span: body.span });
|
||||
} else if let Some(g) = &arm.guard {
|
||||
self.dcx().emit_err(NeverPatternWithGuard { span: g.span });
|
||||
self.dcx().emit_err(NeverPatternWithGuard { span: g.span() });
|
||||
}
|
||||
|
||||
// We add a fake `loop {}` arm body so that it typecks to `!`. The mir lowering of never
|
||||
|
||||
@@ -133,8 +133,11 @@ fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
|
||||
self.lower_range_end(end, e2.is_some()),
|
||||
);
|
||||
}
|
||||
PatKind::Guard(inner, cond) => {
|
||||
break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond));
|
||||
PatKind::Guard(inner, guard) => {
|
||||
break hir::PatKind::Guard(
|
||||
self.lower_pat(inner),
|
||||
self.lower_expr(&guard.cond),
|
||||
);
|
||||
}
|
||||
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
|
||||
PatKind::Rest => {
|
||||
|
||||
@@ -1943,12 +1943,12 @@ fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
self.print_expr(e, FixupContext::default());
|
||||
}
|
||||
}
|
||||
PatKind::Guard(subpat, condition) => {
|
||||
PatKind::Guard(subpat, guard) => {
|
||||
self.popen();
|
||||
self.print_pat(subpat);
|
||||
self.space();
|
||||
self.word_space("if");
|
||||
self.print_expr(condition, FixupContext::default());
|
||||
self.print_expr(&guard.cond, FixupContext::default());
|
||||
self.pclose();
|
||||
}
|
||||
PatKind::Slice(elts) => {
|
||||
|
||||
@@ -891,9 +891,9 @@ fn print_arm(&mut self, arm: &ast::Arm) {
|
||||
self.print_outer_attributes(&arm.attrs);
|
||||
self.print_pat(&arm.pat);
|
||||
self.space();
|
||||
if let Some(e) = &arm.guard {
|
||||
if let Some(guard) = &arm.guard {
|
||||
self.word_space("if");
|
||||
self.print_expr(e, FixupContext::default());
|
||||
self.print_expr(&guard.cond, FixupContext::default());
|
||||
self.space();
|
||||
}
|
||||
|
||||
|
||||
@@ -582,6 +582,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
|
||||
r#"kernel_address = "on|off""#,
|
||||
r#"cfi = "on|off""#,
|
||||
r#"hwaddress = "on|off""#,
|
||||
r#"kernel_hwaddress = "on|off""#,
|
||||
r#"kcfi = "on|off""#,
|
||||
r#"memory = "on|off""#,
|
||||
r#"memtag = "on|off""#,
|
||||
@@ -648,7 +649,9 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
|
||||
Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
|
||||
Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
|
||||
Some(sym::thread) => apply(SanitizerSet::THREAD),
|
||||
Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
|
||||
Some(sym::hwaddress) | Some(sym::kernel_hwaddress) => {
|
||||
apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
|
||||
}
|
||||
Some(sym::realtime) => match value.value_as_str() {
|
||||
Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
|
||||
Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
|
||||
@@ -673,6 +676,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
|
||||
sym::shadow_call_stack,
|
||||
sym::thread,
|
||||
sym::hwaddress,
|
||||
sym::kernel_hwaddress,
|
||||
sym::realtime,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -120,7 +120,8 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>(
|
||||
if enabled.contains(SanitizerSet::THREAD) {
|
||||
attrs.push(llvm::AttributeKind::SanitizeThread.create_attr(cx.llcx));
|
||||
}
|
||||
if enabled.contains(SanitizerSet::HWADDRESS) {
|
||||
if enabled.contains(SanitizerSet::HWADDRESS) || enabled.contains(SanitizerSet::KERNELHWADDRESS)
|
||||
{
|
||||
attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx));
|
||||
}
|
||||
if enabled.contains(SanitizerSet::SHADOWCALLSTACK) {
|
||||
|
||||
@@ -644,6 +644,10 @@ pub(crate) unsafe fn llvm_optimize(
|
||||
sanitize_kernel_address_recover: config
|
||||
.sanitizer_recover
|
||||
.contains(SanitizerSet::KERNELADDRESS),
|
||||
sanitize_kernel_hwaddress: config.sanitizer.contains(SanitizerSet::KERNELHWADDRESS),
|
||||
sanitize_kernel_hwaddress_recover: config
|
||||
.sanitizer_recover
|
||||
.contains(SanitizerSet::KERNELHWADDRESS),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -210,10 +210,14 @@ pub(crate) fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility {
|
||||
}
|
||||
|
||||
pub(crate) fn set_variable_sanitizer_attrs(llval: &Value, attrs: &CodegenFnAttrs) {
|
||||
if attrs.sanitizers.disabled.contains(SanitizerSet::ADDRESS) {
|
||||
if attrs.sanitizers.disabled.contains(SanitizerSet::ADDRESS)
|
||||
|| attrs.sanitizers.disabled.contains(SanitizerSet::KERNELADDRESS)
|
||||
{
|
||||
unsafe { llvm::LLVMRustSetNoSanitizeAddress(llval) };
|
||||
}
|
||||
if attrs.sanitizers.disabled.contains(SanitizerSet::HWADDRESS) {
|
||||
if attrs.sanitizers.disabled.contains(SanitizerSet::HWADDRESS)
|
||||
|| attrs.sanitizers.disabled.contains(SanitizerSet::KERNELHWADDRESS)
|
||||
{
|
||||
unsafe { llvm::LLVMRustSetNoSanitizeHWAddress(llval) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ fn dbg_scope_fn(
|
||||
// FIXME(eddyb) does this need to be separate from `loc.line` for some reason?
|
||||
let scope_line = loc.line;
|
||||
|
||||
let mut flags = DIFlags::FlagPrototyped;
|
||||
let mut flags = DIFlags::FlagPrototyped | DIFlags::FlagAllCallsDescribed;
|
||||
|
||||
if fn_abi.ret.layout.is_uninhabited() {
|
||||
flags |= DIFlags::FlagNoReturn;
|
||||
@@ -494,6 +494,9 @@ fn dbg_scope_fn(
|
||||
// LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition.
|
||||
// When we use this `decl` below, the subprogram definition gets created at the CU level
|
||||
// with a DW_AT_specification pointing back to the type's declaration.
|
||||
// FlagAllCallsDescribed cannot appear on the method declaration DIE
|
||||
// because it has no body, which LLVM's verifier rejects.
|
||||
let decl_flags = flags & !DIFlags::FlagAllCallsDescribed;
|
||||
let decl = is_method.then(|| unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateMethod(
|
||||
DIB(self),
|
||||
@@ -505,7 +508,7 @@ fn dbg_scope_fn(
|
||||
file_metadata,
|
||||
loc.line,
|
||||
function_type_metadata,
|
||||
flags,
|
||||
decl_flags,
|
||||
spflags & !DISPFlags::SPFlagDefinition,
|
||||
template_parameters,
|
||||
)
|
||||
|
||||
@@ -464,6 +464,8 @@ pub(crate) struct SanitizerOptions {
|
||||
pub sanitize_hwaddress_recover: bool,
|
||||
pub sanitize_kernel_address: bool,
|
||||
pub sanitize_kernel_address_recover: bool,
|
||||
pub sanitize_kernel_hwaddress: bool,
|
||||
pub sanitize_kernel_hwaddress_recover: bool,
|
||||
}
|
||||
|
||||
/// LLVMRustRelocModel
|
||||
@@ -779,6 +781,7 @@ pub(crate) struct DIFlags: u32 {
|
||||
const FlagNonTrivial = (1 << 26);
|
||||
const FlagBigEndian = (1 << 27);
|
||||
const FlagLittleEndian = (1 << 28);
|
||||
const FlagAllCallsDescribed = (1 << 29);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
||||
&ret.clone().into(),
|
||||
ReturnContinuation::Stop { cleanup: false },
|
||||
)?;
|
||||
ecx.storage_live_for_always_live_locals()?;
|
||||
ecx.push_stack_frame_done()?;
|
||||
|
||||
// The main interpreter loop.
|
||||
while ecx.step()? {
|
||||
|
||||
@@ -404,166 +404,158 @@ pub fn init_stack_frame(
|
||||
|
||||
// Push the "raw" frame -- this leaves locals uninitialized.
|
||||
self.push_stack_frame_raw(instance, body, destination, cont)?;
|
||||
let preamble_span = self.frame().loc.unwrap_right(); // the span used for preamble errors
|
||||
|
||||
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
||||
// To this end, we wrap it all in a `try` block.
|
||||
let res: InterpResult<'tcx> = try {
|
||||
trace!(
|
||||
"caller ABI: {:#?}, args: {:#?}",
|
||||
caller_fn_abi,
|
||||
args.iter()
|
||||
.map(|arg| (
|
||||
arg.layout().ty,
|
||||
match arg {
|
||||
FnArg::Copy(op) => format!("copy({op:?})"),
|
||||
FnArg::InPlace(mplace) => format!("in-place({mplace:?})"),
|
||||
}
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
trace!(
|
||||
"spread_arg: {:?}, locals: {:#?}",
|
||||
body.spread_arg,
|
||||
body.args_iter()
|
||||
.map(|local| (
|
||||
local,
|
||||
self.layout_of_local(self.frame(), local, None).unwrap().ty,
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// In principle, we have two iterators: Where the arguments come from, and where
|
||||
// they go to.
|
||||
|
||||
// The "where they come from" part is easy, we expect the caller to do any special handling
|
||||
// that might be required here (e.g. for untupling).
|
||||
// If `with_caller_location` is set we pretend there is an extra argument (that
|
||||
// we will not pass; our `caller_location` intrinsic implementation walks the stack instead).
|
||||
assert_eq!(
|
||||
args.len() + if with_caller_location { 1 } else { 0 },
|
||||
caller_fn_abi.args.len(),
|
||||
"mismatch between caller ABI and caller arguments",
|
||||
);
|
||||
let mut caller_args = args
|
||||
.iter()
|
||||
.zip(caller_fn_abi.args.iter())
|
||||
.filter(|arg_and_abi| !arg_and_abi.1.is_ignore());
|
||||
|
||||
// Now we have to spread them out across the callee's locals,
|
||||
// taking into account the `spread_arg`. If we could write
|
||||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
|
||||
// Determine whether there is a special VaList argument. This is always the
|
||||
// last argument, and since arguments start at index 1 that's `arg_count`.
|
||||
let va_list_arg =
|
||||
callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count));
|
||||
for local in body.args_iter() {
|
||||
// Construct the destination place for this argument. At this point all
|
||||
// locals are still dead, so we cannot construct a `PlaceTy`.
|
||||
let dest = mir::Place::from(local);
|
||||
// `layout_of_local` does more than just the instantiation we need to get the
|
||||
// type, but the result gets cached so this avoids calling the instantiation
|
||||
// query *again* the next time this local is accessed.
|
||||
let ty = self.layout_of_local(self.frame(), local, None)?.ty;
|
||||
if Some(local) == va_list_arg {
|
||||
// This is the last callee-side argument of a variadic function.
|
||||
// This argument is a VaList holding the remaining caller-side arguments.
|
||||
self.storage_live(local)?;
|
||||
|
||||
let place = self.eval_place(dest)?;
|
||||
let mplace = self.force_allocation(&place)?;
|
||||
|
||||
// Consume the remaining arguments by putting them into the variable argument
|
||||
// list.
|
||||
let varargs = self.allocate_varargs(
|
||||
&mut caller_args,
|
||||
// "Ignored" arguments aren't actually passed, so the callee should also
|
||||
// ignore them. (`pass_argument` does this for regular arguments.)
|
||||
(&mut callee_args_abis).filter(|(_, abi)| !abi.is_ignore()),
|
||||
)?;
|
||||
// When the frame is dropped, these variable arguments are deallocated.
|
||||
self.frame_mut().va_list = varargs.clone();
|
||||
let key = self.va_list_ptr(varargs.into());
|
||||
|
||||
// Zero the VaList, so it is fully initialized.
|
||||
self.write_bytes_ptr(
|
||||
mplace.ptr(),
|
||||
(0..mplace.layout.size.bytes()).map(|_| 0u8),
|
||||
)?;
|
||||
|
||||
// Store the "key" pointer in the right field.
|
||||
let key_mplace = self.va_list_key_field(&mplace)?;
|
||||
self.write_pointer(key, &key_mplace)?;
|
||||
} else if Some(local) == body.spread_arg {
|
||||
// Make the local live once, then fill in the value field by field.
|
||||
self.storage_live(local)?;
|
||||
// Must be a tuple
|
||||
let ty::Tuple(fields) = ty.kind() else {
|
||||
span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}")
|
||||
};
|
||||
for (i, field_ty) in fields.iter().enumerate() {
|
||||
let dest = dest.project_deeper(
|
||||
&[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)],
|
||||
*self.tcx,
|
||||
);
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
field_ty,
|
||||
/* already_live */ true,
|
||||
)?;
|
||||
trace!(
|
||||
"caller ABI: {:#?}, args: {:#?}",
|
||||
caller_fn_abi,
|
||||
args.iter()
|
||||
.map(|arg| (
|
||||
arg.layout().ty,
|
||||
match arg {
|
||||
FnArg::Copy(op) => format!("copy({op:?})"),
|
||||
FnArg::InPlace(mplace) => format!("in-place({mplace:?})"),
|
||||
}
|
||||
} else {
|
||||
// Normal argument. Cannot mark it as live yet, it might be unsized!
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
trace!(
|
||||
"spread_arg: {:?}, locals: {:#?}",
|
||||
body.spread_arg,
|
||||
body.args_iter()
|
||||
.map(|local| (local, self.layout_of_local(self.frame(), local, None).unwrap().ty,))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// In principle, we have two iterators: Where the arguments come from, and where
|
||||
// they go to.
|
||||
|
||||
// The "where they come from" part is easy, we expect the caller to do any special handling
|
||||
// that might be required here (e.g. for untupling).
|
||||
// If `with_caller_location` is set we pretend there is an extra argument (that
|
||||
// we will not pass; our `caller_location` intrinsic implementation walks the stack instead).
|
||||
assert_eq!(
|
||||
args.len() + if with_caller_location { 1 } else { 0 },
|
||||
caller_fn_abi.args.len(),
|
||||
"mismatch between caller ABI and caller arguments",
|
||||
);
|
||||
let mut caller_args = args
|
||||
.iter()
|
||||
.zip(caller_fn_abi.args.iter())
|
||||
.filter(|arg_and_abi| !arg_and_abi.1.is_ignore());
|
||||
|
||||
// Now we have to spread them out across the callee's locals,
|
||||
// taking into account the `spread_arg`. If we could write
|
||||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
|
||||
// Determine whether there is a special VaList argument. This is always the
|
||||
// last argument, and since arguments start at index 1 that's `arg_count`.
|
||||
let va_list_arg = callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count));
|
||||
for local in body.args_iter() {
|
||||
// Update the span that we show in case of an error to point to this argument.
|
||||
self.frame_mut().loc = Right(body.local_decls[local].source_info.span);
|
||||
// Construct the destination place for this argument. At this point all
|
||||
// locals are still dead, so we cannot construct a `PlaceTy`.
|
||||
let dest = mir::Place::from(local);
|
||||
// `layout_of_local` does more than just the instantiation we need to get the
|
||||
// type, but the result gets cached so this avoids calling the instantiation
|
||||
// query *again* the next time this local is accessed.
|
||||
let ty = self.layout_of_local(self.frame(), local, None)?.ty;
|
||||
if Some(local) == va_list_arg {
|
||||
// This is the last callee-side argument of a variadic function.
|
||||
// This argument is a VaList holding the remaining caller-side arguments.
|
||||
self.storage_live(local)?;
|
||||
|
||||
let place = self.eval_place(dest)?;
|
||||
let mplace = self.force_allocation(&place)?;
|
||||
|
||||
// Consume the remaining arguments by putting them into the variable argument
|
||||
// list.
|
||||
let varargs = self.allocate_varargs(
|
||||
&mut caller_args,
|
||||
// "Ignored" arguments aren't actually passed, so the callee should also
|
||||
// ignore them. (`pass_argument` does this for regular arguments.)
|
||||
(&mut callee_args_abis).filter(|(_, abi)| !abi.is_ignore()),
|
||||
)?;
|
||||
// When the frame is dropped, these variable arguments are deallocated.
|
||||
self.frame_mut().va_list = varargs.clone();
|
||||
let key = self.va_list_ptr(varargs.into());
|
||||
|
||||
// Zero the VaList, so it is fully initialized.
|
||||
self.write_bytes_ptr(mplace.ptr(), (0..mplace.layout.size.bytes()).map(|_| 0u8))?;
|
||||
|
||||
// Store the "key" pointer in the right field.
|
||||
let key_mplace = self.va_list_key_field(&mplace)?;
|
||||
self.write_pointer(key, &key_mplace)?;
|
||||
} else if Some(local) == body.spread_arg {
|
||||
// Make the local live once, then fill in the value field by field.
|
||||
self.storage_live(local)?;
|
||||
// Must be a tuple
|
||||
let ty::Tuple(fields) = ty.kind() else {
|
||||
span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}")
|
||||
};
|
||||
for (i, field_ty) in fields.iter().enumerate() {
|
||||
let dest = dest.project_deeper(
|
||||
&[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)],
|
||||
*self.tcx,
|
||||
);
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
ty,
|
||||
/* already_live */ false,
|
||||
field_ty,
|
||||
/* already_live */ true,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
// Normal argument. Cannot mark it as live yet, it might be unsized!
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
ty,
|
||||
/* already_live */ false,
|
||||
)?;
|
||||
}
|
||||
// If the callee needs a caller location, pretend we consume one more argument from the ABI.
|
||||
if instance.def.requires_caller_location(*self.tcx) {
|
||||
callee_args_abis.next().unwrap();
|
||||
}
|
||||
// Now we should have no more caller args or callee arg ABIs.
|
||||
assert!(
|
||||
callee_args_abis.next().is_none(),
|
||||
"mismatch between callee ABI and callee body arguments"
|
||||
);
|
||||
if caller_args.next().is_some() {
|
||||
throw_ub_format!("calling a function with more arguments than it expected");
|
||||
}
|
||||
// Don't forget to check the return type!
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
|
||||
throw_ub!(AbiMismatchReturn {
|
||||
caller_ty: caller_fn_abi.ret.layout.ty,
|
||||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Protect return place for in-place return value passing.
|
||||
// We only need to protect anything if this is actually an in-memory place.
|
||||
if let Some(mplace) = destination_mplace {
|
||||
M::protect_in_place_function_argument(self, &mplace)?;
|
||||
}
|
||||
// Don't forget to check the return type!
|
||||
self.frame_mut().loc = Right(body.local_decls[mir::RETURN_PLACE].source_info.span);
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
|
||||
throw_ub!(AbiMismatchReturn {
|
||||
caller_ty: caller_fn_abi.ret.layout.ty,
|
||||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
// Protect return place for in-place return value passing.
|
||||
// We only need to protect anything if this is actually an in-memory place.
|
||||
if let Some(mplace) = destination_mplace {
|
||||
M::protect_in_place_function_argument(self, &mplace)?;
|
||||
}
|
||||
|
||||
// Don't forget to mark "initially live" locals as live.
|
||||
self.storage_live_for_always_live_locals()?;
|
||||
};
|
||||
res.inspect_err_kind(|_| {
|
||||
// Don't show the incomplete stack frame in the error stacktrace.
|
||||
self.stack_mut().pop();
|
||||
})
|
||||
// For the final checks, use same span as preamble since it is unclear what else to do.
|
||||
self.frame_mut().loc = Right(preamble_span);
|
||||
// If the callee needs a caller location, pretend we consume one more argument from the ABI.
|
||||
if instance.def.requires_caller_location(*self.tcx) {
|
||||
callee_args_abis.next().unwrap();
|
||||
}
|
||||
// Now we should have no more caller args or callee arg ABIs.
|
||||
assert!(
|
||||
callee_args_abis.next().is_none(),
|
||||
"mismatch between callee ABI and callee body arguments"
|
||||
);
|
||||
if caller_args.next().is_some() {
|
||||
throw_ub_format!("calling a function with more arguments than it expected");
|
||||
}
|
||||
|
||||
// Done!
|
||||
self.push_stack_frame_done()
|
||||
}
|
||||
|
||||
/// Initiate a call to this function -- pushing the stack frame and initializing the arguments.
|
||||
|
||||
@@ -381,7 +381,7 @@ pub(crate) fn push_stack_frame_raw(
|
||||
let locals = IndexVec::from_elem(dead_local, &body.local_decls);
|
||||
let pre_frame = Frame {
|
||||
body,
|
||||
loc: Right(body.span), // Span used for errors caused during preamble.
|
||||
loc: Right(self.tcx.def_span(body.source.def_id())), // Span used for errors caused during preamble.
|
||||
return_cont,
|
||||
return_place: return_place.clone(),
|
||||
locals,
|
||||
@@ -408,7 +408,6 @@ pub(crate) fn push_stack_frame_raw(
|
||||
|
||||
// Finish things up.
|
||||
M::after_stack_push(self)?;
|
||||
self.frame_mut().loc = Left(mir::Location::START);
|
||||
// `tracing_separate_thread` is used to instruct the tracing_chrome [tracing::Layer] in Miri
|
||||
// to put the "frame" span on a separate trace thread/line than other spans, to make the
|
||||
// visualization in <https://ui.perfetto.dev> easier to interpret. It is set to a value of
|
||||
@@ -466,9 +465,11 @@ pub(super) fn cleanup_stack_frame(
|
||||
}
|
||||
}
|
||||
|
||||
/// In the current stack frame, mark all locals as live that are not arguments and don't have
|
||||
/// `Storage*` annotations (this includes the return place).
|
||||
pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> {
|
||||
/// Call this after `push_stack_frame_raw` and when all the other setup that needs to be done
|
||||
/// is completed.
|
||||
pub(crate) fn push_stack_frame_done(&mut self) -> InterpResult<'tcx> {
|
||||
// Mark all locals as live that are not arguments and don't have `Storage*` annotations
|
||||
// (this includes the return place, but not the arguments).
|
||||
self.storage_live(mir::RETURN_PLACE)?;
|
||||
|
||||
let body = self.body();
|
||||
@@ -478,6 +479,10 @@ pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tc
|
||||
self.storage_live(local)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Get ready to execute the first instruction in the stack frame.
|
||||
self.frame_mut().loc = Left(mir::Location::START);
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1532,6 +1532,7 @@ fn visit_value(&mut self, val: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'t
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// The internal core entry point for all validation operations.
|
||||
fn validate_operand_internal(
|
||||
&mut self,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
@@ -1539,6 +1540,7 @@ fn validate_operand_internal(
|
||||
ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Path<'tcx>>>,
|
||||
ctfe_mode: Option<CtfeValidationMode>,
|
||||
reset_provenance_and_padding: bool,
|
||||
start_in_may_dangle: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty);
|
||||
|
||||
@@ -1556,7 +1558,7 @@ fn validate_operand_internal(
|
||||
ecx,
|
||||
reset_provenance_and_padding,
|
||||
data_bytes: reset_padding.then_some(RangeSet(Vec::new())),
|
||||
may_dangle: false,
|
||||
may_dangle: start_in_may_dangle,
|
||||
};
|
||||
v.visit_value(val)?;
|
||||
v.reset_padding(val)?;
|
||||
@@ -1599,6 +1601,7 @@ pub(crate) fn const_validate_operand(
|
||||
Some(ref_tracking),
|
||||
Some(ctfe_mode),
|
||||
/*reset_provenance*/ false,
|
||||
/*start_in_may_dangle*/ false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1629,6 +1632,7 @@ pub fn validate_operand(
|
||||
None,
|
||||
None,
|
||||
reset_provenance_and_padding,
|
||||
/*start_in_may_dangle*/ false,
|
||||
);
|
||||
}
|
||||
// Do a recursive check.
|
||||
@@ -1639,15 +1643,19 @@ pub fn validate_operand(
|
||||
Some(&mut ref_tracking),
|
||||
None,
|
||||
reset_provenance_and_padding,
|
||||
/*start_in_may_dangle*/ false,
|
||||
)?;
|
||||
while let Some((mplace, path)) = ref_tracking.todo.pop() {
|
||||
// Things behind reference do *not* have the provenance reset.
|
||||
// Things behind reference do *not* have the provenance reset. In fact
|
||||
// we treat the entire thing as being inside MaybeDangling, i.e., references
|
||||
// do not have to be dereferenceable.
|
||||
self.validate_operand_internal(
|
||||
&mplace.into(),
|
||||
path,
|
||||
Some(&mut ref_tracking),
|
||||
None, // no further recursion
|
||||
None,
|
||||
/*reset_provenance_and_padding*/ false,
|
||||
/*start_in_may_dangle*/ true,
|
||||
)?;
|
||||
}
|
||||
interp_ok(())
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(unqualified_local_imports)]
|
||||
#![feature(yeet_expr)]
|
||||
#![warn(unqualified_local_imports)]
|
||||
|
||||
@@ -724,7 +724,7 @@ pub fn compile_declarative_macro(
|
||||
let args = p.parse_token_tree();
|
||||
check_args_parens(sess, sym::attr, &args);
|
||||
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
|
||||
check_emission(check_lhs(sess, node_id, &args));
|
||||
check_emission(check_lhs(sess, features, node_id, &args));
|
||||
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
|
||||
return dummy_syn_ext(guar);
|
||||
}
|
||||
@@ -773,7 +773,7 @@ pub fn compile_declarative_macro(
|
||||
};
|
||||
let lhs_tt = p.parse_token_tree();
|
||||
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
|
||||
check_emission(check_lhs(sess, node_id, &lhs_tt));
|
||||
check_emission(check_lhs(sess, features, node_id, &lhs_tt));
|
||||
if let Err(e) = p.expect(exp!(FatArrow)) {
|
||||
return dummy_syn_ext(e.emit());
|
||||
}
|
||||
@@ -870,21 +870,27 @@ fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
|
||||
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
|
||||
fn check_lhs(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
node_id: NodeId,
|
||||
lhs: &mbe::TokenTree,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let e1 = check_lhs_nt_follows(sess, features, node_id, lhs);
|
||||
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
|
||||
e1.and(e2)
|
||||
}
|
||||
|
||||
fn check_lhs_nt_follows(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
node_id: NodeId,
|
||||
lhs: &mbe::TokenTree,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
// lhs is going to be like TokenTree::Delimited(...), where the
|
||||
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
|
||||
if let mbe::TokenTree::Delimited(.., delimited) = lhs {
|
||||
check_matcher(sess, node_id, &delimited.tts)
|
||||
check_matcher(sess, features, node_id, &delimited.tts)
|
||||
} else {
|
||||
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
|
||||
Err(sess.dcx().span_err(lhs.span(), msg))
|
||||
@@ -989,12 +995,13 @@ fn check_rhs(sess: &Session, rhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed
|
||||
|
||||
fn check_matcher(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
node_id: NodeId,
|
||||
matcher: &[mbe::TokenTree],
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let first_sets = FirstSets::new(matcher);
|
||||
let empty_suffix = TokenSet::empty();
|
||||
check_matcher_core(sess, node_id, &first_sets, matcher, &empty_suffix)?;
|
||||
check_matcher_core(sess, features, node_id, &first_sets, matcher, &empty_suffix)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1331,6 +1338,7 @@ fn add_all(&mut self, other: &Self) {
|
||||
// see `FirstSets::new`.
|
||||
fn check_matcher_core<'tt>(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
node_id: NodeId,
|
||||
first_sets: &FirstSets<'tt>,
|
||||
matcher: &'tt [mbe::TokenTree],
|
||||
@@ -1369,6 +1377,17 @@ fn check_matcher_core<'tt>(
|
||||
| TokenTree::MetaVar(..)
|
||||
| TokenTree::MetaVarDecl { .. }
|
||||
| TokenTree::MetaVarExpr(..) => {
|
||||
if let TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } = token
|
||||
&& !features.macro_guard_matcher()
|
||||
{
|
||||
feature_err(
|
||||
sess,
|
||||
sym::macro_guard_matcher,
|
||||
token.span(),
|
||||
"`guard` fragments in macro are unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
if token_can_be_followed_by_any(token) {
|
||||
// don't need to track tokens that work with any,
|
||||
last.replace_with_irrelevant();
|
||||
@@ -1385,7 +1404,7 @@ fn check_matcher_core<'tt>(
|
||||
d.delim.as_close_token_kind(),
|
||||
span.close,
|
||||
));
|
||||
check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?;
|
||||
check_matcher_core(sess, features, node_id, first_sets, &d.tts, &my_suffix)?;
|
||||
// don't track non NT tokens
|
||||
last.replace_with_irrelevant();
|
||||
|
||||
@@ -1417,7 +1436,14 @@ fn check_matcher_core<'tt>(
|
||||
// At this point, `suffix_first` is built, and
|
||||
// `my_suffix` is some TokenSet that we can use
|
||||
// for checking the interior of `seq_rep`.
|
||||
let next = check_matcher_core(sess, node_id, first_sets, &seq_rep.tts, my_suffix)?;
|
||||
let next = check_matcher_core(
|
||||
sess,
|
||||
features,
|
||||
node_id,
|
||||
first_sets,
|
||||
&seq_rep.tts,
|
||||
my_suffix,
|
||||
)?;
|
||||
if next.maybe_empty {
|
||||
last.add_all(&next);
|
||||
} else {
|
||||
@@ -1609,7 +1635,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
|
||||
}
|
||||
}
|
||||
NonterminalKind::Pat(PatParam { .. }) => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`if let`", "`in`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
FatArrow | Comma | Eq | Or => IsInFollow::Yes,
|
||||
@@ -1618,11 +1644,12 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
|
||||
}
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
},
|
||||
TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } => IsInFollow::Yes,
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
NonterminalKind::Pat(PatWithOr) => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`if let`", "`in`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
FatArrow | Comma | Eq => IsInFollow::Yes,
|
||||
@@ -1631,6 +1658,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
|
||||
}
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
},
|
||||
TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } => IsInFollow::Yes,
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
NonterminalKind::Guard => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`{`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
FatArrow | Comma | OpenBrace => IsInFollow::Yes,
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
},
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::hygiene::{LocalExpnId, Transparency};
|
||||
use rustc_span::{
|
||||
Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
|
||||
BytePos, Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, kw, sym,
|
||||
with_metavar_spans,
|
||||
};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
@@ -556,6 +557,19 @@ fn transcribe_pnr<'tx>(
|
||||
ParseNtResult::Vis(vis) => {
|
||||
mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
|
||||
}
|
||||
ParseNtResult::Guard(guard) => {
|
||||
// FIXME(macro_guard_matcher):
|
||||
// Perhaps it would be better to treat the leading `if` as part of `ast::Guard` during parsing?
|
||||
// Currently they are separate, but in macros we match and emit the leading `if` for `:guard` matchers, which creates some inconsistency.
|
||||
|
||||
let leading_if_span =
|
||||
guard.span_with_leading_if.with_hi(guard.span_with_leading_if.lo() + BytePos(2));
|
||||
let mut ts =
|
||||
TokenStream::token_alone(token::Ident(kw::If, IdentIsRaw::No), leading_if_span);
|
||||
ts.push_stream(TokenStream::from_ast(&guard.cond));
|
||||
|
||||
mk_delimited(guard.span_with_leading_if, MetaVarKind::Guard, ts)
|
||||
}
|
||||
};
|
||||
|
||||
tscx.result.push(tt);
|
||||
|
||||
@@ -773,7 +773,7 @@ pub struct BuiltinAttribute {
|
||||
DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature)
|
||||
),
|
||||
gated!(
|
||||
sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
|
||||
sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kernel_hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
|
||||
EncodeCrossCrate::No, sanitize, experimental!(sanitize),
|
||||
),
|
||||
gated!(
|
||||
|
||||
@@ -569,6 +569,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(unstable, macro_attr, "1.91.0", Some(143547)),
|
||||
/// Allow `macro_rules!` derive rules
|
||||
(unstable, macro_derive, "1.91.0", Some(143549)),
|
||||
/// Allow `$x:guard` matcher in macros
|
||||
(unstable, macro_guard_matcher, "CURRENT_RUSTC_VERSION", Some(153104)),
|
||||
/// Give access to additional metadata about declarative macro meta-variables.
|
||||
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
||||
/// Provides a way to concatenate identifiers using metavariable expressions.
|
||||
|
||||
@@ -138,7 +138,6 @@ fn create_mapping<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sig_id: DefId,
|
||||
def_id: LocalDefId,
|
||||
args: &[ty::GenericArg<'tcx>],
|
||||
) -> FxHashMap<u32, u32> {
|
||||
let mut mapping: FxHashMap<u32, u32> = Default::default();
|
||||
|
||||
@@ -176,13 +175,6 @@ fn create_mapping<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
// If there are still unmapped lifetimes left and we are to map types and maybe self
|
||||
// then skip them, now it is the case when we generated more lifetimes then needed.
|
||||
// FIXME(fn_delegation): proper support for late bound lifetimes.
|
||||
while args_index < args.len() && args[args_index].as_region().is_some() {
|
||||
args_index += 1;
|
||||
}
|
||||
|
||||
// If self after lifetimes insert mapping, relying that self is at 0 in sig parent.
|
||||
if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes) {
|
||||
mapping.insert(0, args_index as u32);
|
||||
@@ -511,7 +503,7 @@ fn create_folder_and_args<'tcx>(
|
||||
child_args: &'tcx [ty::GenericArg<'tcx>],
|
||||
) -> (ParamIndexRemapper<'tcx>, Vec<ty::GenericArg<'tcx>>) {
|
||||
let args = create_generic_args(tcx, sig_id, def_id, parent_args, child_args);
|
||||
let remap_table = create_mapping(tcx, sig_id, def_id, &args);
|
||||
let remap_table = create_mapping(tcx, sig_id, def_id);
|
||||
|
||||
(ParamIndexRemapper { tcx, remap_table }, args)
|
||||
}
|
||||
|
||||
@@ -1323,6 +1323,12 @@ pub fn insert(&mut self, elem: T) -> bool {
|
||||
self.bit_set.insert(elem)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert_range(&mut self, elems: Range<T>) {
|
||||
self.ensure(elems.end.index());
|
||||
self.bit_set.insert_range(elems);
|
||||
}
|
||||
|
||||
/// Returns `true` if the set has changed.
|
||||
#[inline]
|
||||
pub fn remove(&mut self, elem: T) -> bool {
|
||||
@@ -1330,6 +1336,16 @@ pub fn remove(&mut self, elem: T) -> bool {
|
||||
self.bit_set.remove(elem)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.bit_set.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn count(&self) -> usize {
|
||||
self.bit_set.count()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bit_set.is_empty()
|
||||
@@ -1341,6 +1357,14 @@ pub fn contains(&self, elem: T) -> bool {
|
||||
self.bit_set.words.get(word_index).is_some_and(|word| (word & mask) != 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains_any(&self, elems: Range<T>) -> bool {
|
||||
elems.start.index() < self.bit_set.domain_size
|
||||
&& self
|
||||
.bit_set
|
||||
.contains_any(elems.start..T::new(elems.end.index().min(self.bit_set.domain_size)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> BitIter<'_, T> {
|
||||
self.bit_set.iter()
|
||||
|
||||
@@ -540,6 +540,8 @@ struct LLVMRustSanitizerOptions {
|
||||
bool SanitizeHWAddressRecover;
|
||||
bool SanitizeKernelAddress;
|
||||
bool SanitizeKernelAddressRecover;
|
||||
bool SanitizeKernelHWAddress;
|
||||
bool SanitizeKernelHWAddressRecover;
|
||||
};
|
||||
|
||||
extern "C" typedef void (*registerEnzymeAndPassPipelineFn)(
|
||||
@@ -767,13 +769,15 @@ extern "C" LLVMRustResult LLVMRustOptimize(
|
||||
!TM->getTargetTriple().isOSWindows()));
|
||||
});
|
||||
}
|
||||
if (SanitizerOptions->SanitizeHWAddress) {
|
||||
if (SanitizerOptions->SanitizeHWAddress ||
|
||||
SanitizerOptions->SanitizeKernelHWAddress) {
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level,
|
||||
ThinOrFullLTOPhase phase) {
|
||||
HWAddressSanitizerOptions opts(
|
||||
/*CompileKernel=*/false,
|
||||
SanitizerOptions->SanitizeHWAddressRecover,
|
||||
SanitizerOptions->SanitizeKernelHWAddress,
|
||||
SanitizerOptions->SanitizeHWAddressRecover ||
|
||||
SanitizerOptions->SanitizeKernelHWAddressRecover,
|
||||
/*DisableOptimization=*/false);
|
||||
MPM.addPass(HWAddressSanitizerPass(opts));
|
||||
});
|
||||
|
||||
@@ -779,6 +779,7 @@ ASSERT_DIFLAG_VALUE(FlagThunk, 1 << 25);
|
||||
ASSERT_DIFLAG_VALUE(FlagNonTrivial, 1 << 26);
|
||||
ASSERT_DIFLAG_VALUE(FlagBigEndian, 1 << 27);
|
||||
ASSERT_DIFLAG_VALUE(FlagLittleEndian, 1 << 28);
|
||||
static_assert(DINode::DIFlags::FlagAllCallsDescribed == (1 << 29));
|
||||
ASSERT_DIFLAG_VALUE(FlagIndirectVirtualBase, (1 << 2) | (1 << 5));
|
||||
#undef ASSERT_DIFLAG_VALUE
|
||||
|
||||
@@ -791,7 +792,7 @@ ASSERT_DIFLAG_VALUE(FlagIndirectVirtualBase, (1 << 2) | (1 << 5));
|
||||
// to copying each bit/subvalue.
|
||||
static DINode::DIFlags fromRust(LLVMDIFlags Flags) {
|
||||
// Check that all set bits are covered by the static assertions above.
|
||||
const unsigned UNKNOWN_BITS = (1 << 31) | (1 << 30) | (1 << 29) | (1 << 21);
|
||||
const unsigned UNKNOWN_BITS = (1 << 31) | (1 << 30) | (1 << 21);
|
||||
if (Flags & UNKNOWN_BITS) {
|
||||
report_fatal_error("bad LLVMDIFlags");
|
||||
}
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_span::{ErrorGuaranteed, Spanned};
|
||||
|
||||
use crate::mir::interpret::EvalToValTreeResult;
|
||||
use crate::mir::mono::{MonoItem, NormalizationErrorInMono};
|
||||
use crate::traits::solve;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use crate::{mir, traits};
|
||||
use crate::{mir, thir, traits};
|
||||
|
||||
/// Internal implementation detail of [`Erased`].
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -101,192 +100,42 @@ pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T {
|
||||
// FIXME(#151565): Using `T: ?Sized` here should let us remove the separate
|
||||
// impls for fat reference types.
|
||||
impl<T> Erasable for &'_ T {
|
||||
type Storage = [u8; size_of::<&'static ()>()];
|
||||
type Storage = [u8; size_of::<&'_ ()>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for &'_ [T] {
|
||||
type Storage = [u8; size_of::<&'static [()]>()];
|
||||
}
|
||||
|
||||
impl Erasable for &'_ OsStr {
|
||||
type Storage = [u8; size_of::<&'static OsStr>()];
|
||||
type Storage = [u8; size_of::<&'_ [()]>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for &'_ ty::List<T> {
|
||||
type Storage = [u8; size_of::<&'static ty::List<()>>()];
|
||||
type Storage = [u8; size_of::<&'_ ty::List<()>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for &'_ ty::ListWithCachedTypeInfo<T> {
|
||||
type Storage = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()];
|
||||
}
|
||||
|
||||
impl<I: rustc_index::Idx, T> Erasable for &'_ rustc_index::IndexSlice<I, T> {
|
||||
type Storage = [u8; size_of::<&'static rustc_index::IndexSlice<u32, ()>>()];
|
||||
type Storage = [u8; size_of::<&'_ ty::ListWithCachedTypeInfo<()>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> {
|
||||
type Storage = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
|
||||
type Storage = [u8; size_of::<Result<&'_ (), traits::query::NoSolution>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<&'_ [T], traits::query::NoSolution> {
|
||||
type Storage = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
|
||||
type Storage = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<&'_ [T], rustc_errors::ErrorGuaranteed> {
|
||||
type Storage = [u8; size_of::<Result<&'static [()], rustc_errors::ErrorGuaranteed>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<&'_ T, traits::CodegenObligationError> {
|
||||
type Storage = [u8; size_of::<Result<&'static (), traits::CodegenObligationError>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
|
||||
type Storage = [u8; size_of::<Result<&'static (), &'static ty::layout::FnAbiError<'static>>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> {
|
||||
type Storage = [u8; size_of::<
|
||||
Result<(&'static (), crate::thir::ExprId), rustc_errors::ErrorGuaranteed>,
|
||||
>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<Option<ty::Instance<'_>>, rustc_errors::ErrorGuaranteed> {
|
||||
type Storage =
|
||||
[u8; size_of::<Result<Option<ty::Instance<'static>>, rustc_errors::ErrorGuaranteed>>()];
|
||||
}
|
||||
|
||||
impl Erasable
|
||||
for Result<Option<ty::EarlyBinder<'_, ty::Const<'_>>>, rustc_errors::ErrorGuaranteed>
|
||||
{
|
||||
type Storage = [u8; size_of::<
|
||||
Result<Option<ty::EarlyBinder<'static, ty::Const<'static>>>, rustc_errors::ErrorGuaranteed>,
|
||||
>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<ty::GenericArg<'_>, traits::query::NoSolution> {
|
||||
type Storage = [u8; size_of::<Result<ty::GenericArg<'static>, traits::query::NoSolution>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<bool, &ty::layout::LayoutError<'_>> {
|
||||
type Storage = [u8; size_of::<Result<bool, &'static ty::layout::LayoutError<'static>>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>> {
|
||||
type Storage = [u8; size_of::<
|
||||
Result<
|
||||
rustc_abi::TyAndLayout<'static, Ty<'static>>,
|
||||
&'static ty::layout::LayoutError<'static>,
|
||||
>,
|
||||
>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
|
||||
type Storage =
|
||||
[u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Option<(mir::ConstValue, Ty<'_>)> {
|
||||
type Storage = [u8; size_of::<Option<(mir::ConstValue, Ty<'_>)>>()];
|
||||
}
|
||||
|
||||
impl Erasable for EvalToValTreeResult<'_> {
|
||||
type Storage = [u8; size_of::<EvalToValTreeResult<'static>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
|
||||
type Storage =
|
||||
[u8; size_of::<Result<&'static ty::List<Ty<'static>>, ty::util::AlwaysRequiresDrop>>()];
|
||||
}
|
||||
|
||||
impl Erasable
|
||||
for Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>
|
||||
{
|
||||
type Storage = [u8; size_of::<
|
||||
Result<
|
||||
(&'static [Spanned<MonoItem<'static>>], &'static [Spanned<MonoItem<'static>>]),
|
||||
NormalizationErrorInMono,
|
||||
>,
|
||||
>()];
|
||||
}
|
||||
|
||||
impl Erasable for Result<&'_ TokenStream, ()> {
|
||||
type Storage = [u8; size_of::<Result<&'static TokenStream, ()>>()];
|
||||
impl<T> Erasable for Result<&'_ T, ErrorGuaranteed> {
|
||||
type Storage = [u8; size_of::<Result<&'_ (), ErrorGuaranteed>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Option<&'_ T> {
|
||||
type Storage = [u8; size_of::<Option<&'static ()>>()];
|
||||
}
|
||||
|
||||
impl<T> Erasable for Option<&'_ [T]> {
|
||||
type Storage = [u8; size_of::<Option<&'static [()]>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Option<&'_ OsStr> {
|
||||
type Storage = [u8; size_of::<Option<&'static OsStr>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Option<mir::DestructuredConstant<'_>> {
|
||||
type Storage = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()];
|
||||
}
|
||||
|
||||
impl Erasable for ty::ImplTraitHeader<'_> {
|
||||
type Storage = [u8; size_of::<ty::ImplTraitHeader<'static>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Option<ty::EarlyBinder<'_, Ty<'_>>> {
|
||||
type Storage = [u8; size_of::<Option<ty::EarlyBinder<'static, Ty<'static>>>>()];
|
||||
}
|
||||
|
||||
impl Erasable for Option<ty::Value<'_>> {
|
||||
type Storage = [u8; size_of::<Option<ty::Value<'static>>>()];
|
||||
}
|
||||
|
||||
impl Erasable for rustc_hir::MaybeOwner<'_> {
|
||||
type Storage = [u8; size_of::<rustc_hir::MaybeOwner<'static>>()];
|
||||
type Storage = [u8; size_of::<Option<&'_ ()>>()];
|
||||
}
|
||||
|
||||
impl<T: Erasable> Erasable for ty::EarlyBinder<'_, T> {
|
||||
type Storage = T::Storage;
|
||||
}
|
||||
|
||||
impl Erasable for ty::Binder<'_, ty::FnSig<'_>> {
|
||||
type Storage = [u8; size_of::<ty::Binder<'static, ty::FnSig<'static>>>()];
|
||||
}
|
||||
|
||||
impl Erasable for ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>> {
|
||||
type Storage =
|
||||
[u8; size_of::<ty::Binder<'static, ty::CoroutineWitnessTypes<TyCtxt<'static>>>>()];
|
||||
}
|
||||
|
||||
impl Erasable for ty::Binder<'_, &'_ ty::List<Ty<'_>>> {
|
||||
type Storage = [u8; size_of::<ty::Binder<'static, &'static ty::List<Ty<'static>>>>()];
|
||||
}
|
||||
|
||||
impl<T0, T1> Erasable for (&'_ T0, &'_ T1) {
|
||||
type Storage = [u8; size_of::<(&'static (), &'static ())>()];
|
||||
type Storage = [u8; size_of::<(&'_ (), &'_ ())>()];
|
||||
}
|
||||
|
||||
impl<T0> Erasable for (solve::QueryResult<'_>, &'_ T0) {
|
||||
type Storage = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()];
|
||||
}
|
||||
|
||||
impl<T0, T1> Erasable for (&'_ T0, &'_ [T1]) {
|
||||
type Storage = [u8; size_of::<(&'static (), &'static [()])>()];
|
||||
}
|
||||
|
||||
impl<T0, T1> Erasable for (&'_ [T0], &'_ [T1]) {
|
||||
type Storage = [u8; size_of::<(&'static [()], &'static [()])>()];
|
||||
}
|
||||
|
||||
impl<T0> Erasable for (&'_ T0, Result<(), ErrorGuaranteed>) {
|
||||
type Storage = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()];
|
||||
}
|
||||
|
||||
macro_rules! impl_erasable_for_simple_types {
|
||||
macro_rules! impl_erasable_for_types_with_no_type_params {
|
||||
($($ty:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl Erasable for $ty {
|
||||
@@ -296,171 +145,96 @@ impl Erasable for $ty {
|
||||
}
|
||||
}
|
||||
|
||||
// For concrete types with no lifetimes, the erased storage for `Foo` is
|
||||
// `[u8; size_of::<Foo>()]`.
|
||||
impl_erasable_for_simple_types! {
|
||||
// FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this.
|
||||
// For types with no type parameters the erased storage for `Foo` is
|
||||
// `[u8; size_of::<Foo>()]`. ('_ lifetimes are allowed.)
|
||||
impl_erasable_for_types_with_no_type_params! {
|
||||
// tidy-alphabetical-start
|
||||
(&'_ ty::CrateInherentImpls, Result<(), ErrorGuaranteed>),
|
||||
(),
|
||||
bool,
|
||||
(traits::solve::QueryResult<'_>, &'_ traits::solve::inspect::Probe<TyCtxt<'_>>),
|
||||
Option<&'_ OsStr>,
|
||||
Option<&'_ [rustc_hir::PreciseCapturingArgKind<rustc_span::Symbol, rustc_span::Symbol>]>,
|
||||
Option<(mir::ConstValue, Ty<'_>)>,
|
||||
Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>,
|
||||
Option<rustc_abi::Align>,
|
||||
Option<rustc_ast::expand::allocator::AllocatorKind>,
|
||||
Option<rustc_data_structures::svh::Svh>,
|
||||
Option<rustc_hir::ConstStability>,
|
||||
Option<rustc_hir::CoroutineKind>,
|
||||
Option<rustc_hir::DefaultBodyStability>,
|
||||
Option<rustc_hir::Stability>,
|
||||
Option<rustc_data_structures::svh::Svh>,
|
||||
Option<rustc_hir::def::DefKind>,
|
||||
Option<rustc_hir::CoroutineKind>,
|
||||
Option<rustc_hir::HirId>,
|
||||
Option<rustc_middle::middle::stability::DeprecationEntry>,
|
||||
Option<rustc_middle::ty::AsyncDestructor>,
|
||||
Option<rustc_middle::ty::Destructor>,
|
||||
Option<rustc_middle::ty::ImplTraitInTraitData>,
|
||||
Option<rustc_middle::ty::IntrinsicDef>,
|
||||
Option<rustc_middle::ty::ScalarInt>,
|
||||
Option<rustc_span::Span>,
|
||||
Option<rustc_span::def_id::CrateNum>,
|
||||
Option<rustc_span::def_id::DefId>,
|
||||
Option<rustc_span::def_id::LocalDefId>,
|
||||
Option<rustc_span::Span>,
|
||||
Option<rustc_abi::FieldIdx>,
|
||||
Option<rustc_target::spec::PanicStrategy>,
|
||||
Option<ty::EarlyBinder<'_, Ty<'_>>>,
|
||||
Option<ty::Value<'_>>,
|
||||
Option<usize>,
|
||||
Option<rustc_middle::ty::IntrinsicDef>,
|
||||
Option<rustc_abi::Align>,
|
||||
Result<(), rustc_errors::ErrorGuaranteed>,
|
||||
Result<(), rustc_middle::traits::query::NoSolution>,
|
||||
Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
|
||||
Result<rustc_middle::ty::adjustment::CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed>,
|
||||
Result<&'_ TokenStream, ()>,
|
||||
Result<&'_ rustc_target::callconv::FnAbi<'_, Ty<'_>>, &'_ ty::layout::FnAbiError<'_>>,
|
||||
Result<&'_ traits::ImplSource<'_, ()>, traits::CodegenObligationError>,
|
||||
Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop>,
|
||||
Result<(&'_ Steal<thir::Thir<'_>>, thir::ExprId), ErrorGuaranteed>,
|
||||
Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>,
|
||||
Result<(), ErrorGuaranteed>,
|
||||
Result<Option<ty::EarlyBinder<'_, ty::Const<'_>>>, ErrorGuaranteed>,
|
||||
Result<Option<ty::Instance<'_>>, ErrorGuaranteed>,
|
||||
Result<bool, &ty::layout::LayoutError<'_>>,
|
||||
Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled>,
|
||||
Result<mir::ConstValue, mir::interpret::ErrorHandled>,
|
||||
rustc_abi::ReprOptions,
|
||||
rustc_ast::expand::allocator::AllocatorKind,
|
||||
rustc_hir::DefaultBodyStability,
|
||||
rustc_hir::attrs::Deprecation,
|
||||
rustc_hir::attrs::EiiDecl,
|
||||
rustc_hir::attrs::EiiImpl,
|
||||
Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>>,
|
||||
Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
|
||||
Result<rustc_middle::ty::adjustment::CoerceUnsizedInfo, ErrorGuaranteed>,
|
||||
Result<ty::GenericArg<'_>, traits::query::NoSolution>,
|
||||
Ty<'_>,
|
||||
bool,
|
||||
rustc_data_structures::svh::Svh,
|
||||
rustc_errors::ErrorGuaranteed,
|
||||
rustc_hir::Constness,
|
||||
rustc_hir::ConstStability,
|
||||
rustc_hir::def_id::DefId,
|
||||
rustc_hir::def_id::DefIndex,
|
||||
rustc_hir::def_id::LocalDefId,
|
||||
rustc_hir::def_id::LocalModDefId,
|
||||
rustc_hir::def::DefKind,
|
||||
rustc_hir::Defaultness,
|
||||
rustc_hir::definitions::DefKey,
|
||||
rustc_hir::CoroutineKind,
|
||||
rustc_hir::HirId,
|
||||
rustc_hir::IsAsync,
|
||||
rustc_hir::ItemLocalId,
|
||||
rustc_hir::LangItem,
|
||||
rustc_hir::MaybeOwner<'_>,
|
||||
rustc_hir::OpaqueTyOrigin<rustc_hir::def_id::DefId>,
|
||||
rustc_hir::OwnerId,
|
||||
rustc_hir::Stability,
|
||||
rustc_hir::Upvar,
|
||||
rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs,
|
||||
rustc_middle::middle::dependency_format::Linkage,
|
||||
rustc_middle::middle::exported_symbols::SymbolExportInfo,
|
||||
rustc_hir::def::DefKind,
|
||||
rustc_hir::def_id::DefId,
|
||||
rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs,
|
||||
rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault,
|
||||
rustc_middle::middle::resolve_bound_vars::ResolvedArg,
|
||||
rustc_middle::middle::stability::DeprecationEntry,
|
||||
rustc_middle::mir::ConstQualifs,
|
||||
rustc_middle::mir::ConstValue,
|
||||
rustc_middle::mir::interpret::AllocId,
|
||||
rustc_middle::mir::interpret::CtfeProvenance,
|
||||
rustc_middle::mir::interpret::ErrorHandled,
|
||||
rustc_middle::thir::ExprId,
|
||||
rustc_middle::traits::CodegenObligationError,
|
||||
rustc_middle::traits::EvaluationResult,
|
||||
rustc_middle::traits::OverflowError,
|
||||
rustc_middle::traits::query::NoSolution,
|
||||
rustc_middle::traits::WellFormedLoc,
|
||||
rustc_middle::ty::adjustment::CoerceUnsizedInfo,
|
||||
rustc_middle::ty::AssocItem,
|
||||
rustc_middle::ty::AssocContainer,
|
||||
rustc_middle::ty::Asyncness,
|
||||
rustc_middle::ty::AsyncDestructor,
|
||||
rustc_middle::mir::interpret::EvalStaticInitializerRawResult<'_>,
|
||||
rustc_middle::mir::interpret::EvalToValTreeResult<'_>,
|
||||
rustc_middle::mir::mono::MonoItemPartitions<'_>,
|
||||
rustc_middle::traits::query::MethodAutoderefStepsResult<'_>,
|
||||
rustc_middle::ty::AdtDef<'_>,
|
||||
rustc_middle::ty::AnonConstKind,
|
||||
rustc_middle::ty::Destructor,
|
||||
rustc_middle::ty::fast_reject::SimplifiedType,
|
||||
rustc_middle::ty::ImplPolarity,
|
||||
rustc_middle::ty::util::AlwaysRequiresDrop,
|
||||
rustc_middle::ty::AssocItem,
|
||||
rustc_middle::ty::Asyncness,
|
||||
rustc_middle::ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>>,
|
||||
rustc_middle::ty::Binder<'_, ty::FnSig<'_>>,
|
||||
rustc_middle::ty::ClosureTypeInfo<'_>,
|
||||
rustc_middle::ty::Const<'_>,
|
||||
rustc_middle::ty::ConstConditions<'_>,
|
||||
rustc_middle::ty::GenericPredicates<'_>,
|
||||
rustc_middle::ty::ImplTraitHeader<'_>,
|
||||
rustc_middle::ty::ParamEnv<'_>,
|
||||
rustc_middle::ty::SymbolName<'_>,
|
||||
rustc_middle::ty::TypingEnv<'_>,
|
||||
rustc_middle::ty::Visibility<rustc_span::def_id::DefId>,
|
||||
rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs,
|
||||
rustc_session::config::CrateType,
|
||||
rustc_session::config::EntryFnType,
|
||||
rustc_middle::ty::inhabitedness::InhabitedPredicate<'_>,
|
||||
rustc_session::Limits,
|
||||
rustc_session::config::OptLevel,
|
||||
rustc_session::config::SymbolManglingVersion,
|
||||
rustc_session::cstore::CrateDepKind,
|
||||
rustc_session::cstore::ExternCrate,
|
||||
rustc_session::cstore::LinkagePreference,
|
||||
rustc_session::Limits,
|
||||
rustc_session::lint::LintExpectationId,
|
||||
rustc_span::def_id::CrateNum,
|
||||
rustc_span::def_id::DefPathHash,
|
||||
rustc_span::ExpnHash,
|
||||
rustc_span::ExpnId,
|
||||
rustc_span::Span,
|
||||
rustc_span::Symbol,
|
||||
rustc_span::Ident,
|
||||
rustc_target::spec::PanicStrategy,
|
||||
rustc_type_ir::Variance,
|
||||
u32,
|
||||
usize,
|
||||
}
|
||||
|
||||
macro_rules! impl_erasable_for_single_lifetime_types {
|
||||
($($($fake_path:ident)::+),+ $(,)?) => {
|
||||
$(
|
||||
impl<'tcx> Erasable for $($fake_path)::+<'tcx> {
|
||||
type Storage = [u8; size_of::<$($fake_path)::+<'static>>()];
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
// For types containing a single lifetime and no other generics, e.g.
|
||||
// `Foo<'tcx>`, the erased storage is `[u8; size_of::<Foo<'static>>()]`.
|
||||
//
|
||||
// FIXME(#151565): Some of the hand-written impls above that only use one
|
||||
// lifetime can probably be migrated here.
|
||||
impl_erasable_for_single_lifetime_types! {
|
||||
// FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this.
|
||||
rustc_middle::middle::exported_symbols::ExportedSymbol,
|
||||
rustc_middle::mir::Const,
|
||||
rustc_middle::mir::DestructuredConstant,
|
||||
rustc_middle::mir::ConstAlloc,
|
||||
rustc_middle::mir::interpret::GlobalId,
|
||||
rustc_middle::mir::interpret::EvalStaticInitializerRawResult,
|
||||
rustc_middle::mir::mono::MonoItemPartitions,
|
||||
rustc_middle::traits::query::MethodAutoderefStepsResult,
|
||||
rustc_middle::traits::query::type_op::AscribeUserType,
|
||||
rustc_middle::traits::query::type_op::Eq,
|
||||
rustc_middle::traits::query::type_op::ProvePredicate,
|
||||
rustc_middle::traits::query::type_op::Subtype,
|
||||
rustc_middle::ty::AdtDef,
|
||||
rustc_middle::ty::AliasTy,
|
||||
rustc_middle::ty::ClauseKind,
|
||||
rustc_middle::ty::ClosureTypeInfo,
|
||||
rustc_middle::ty::Const,
|
||||
rustc_middle::ty::DestructuredAdtConst,
|
||||
rustc_middle::ty::ExistentialTraitRef,
|
||||
rustc_middle::ty::FnSig,
|
||||
rustc_middle::ty::GenericArg,
|
||||
rustc_middle::ty::GenericPredicates,
|
||||
rustc_middle::ty::ConstConditions,
|
||||
rustc_middle::ty::inhabitedness::InhabitedPredicate,
|
||||
rustc_middle::ty::Instance,
|
||||
rustc_middle::ty::BoundVariableKind,
|
||||
rustc_middle::ty::InstanceKind,
|
||||
rustc_middle::ty::layout::FnAbiError,
|
||||
rustc_middle::ty::layout::LayoutError,
|
||||
rustc_middle::ty::LitToConstInput,
|
||||
rustc_middle::ty::ParamEnv,
|
||||
rustc_middle::ty::TypingEnv,
|
||||
rustc_middle::ty::Predicate,
|
||||
rustc_middle::ty::SymbolName,
|
||||
rustc_middle::ty::TraitRef,
|
||||
rustc_middle::ty::Ty,
|
||||
rustc_middle::ty::UnevaluatedConst,
|
||||
rustc_middle::ty::ValTree,
|
||||
rustc_middle::ty::VtblEntry,
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,
|
||||
BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,
|
||||
FnRetTy, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, RangeLimits,
|
||||
StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind,
|
||||
FnRetTy, Guard, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param,
|
||||
RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind,
|
||||
};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
|
||||
@@ -2767,7 +2767,7 @@ pub fn parse_expr_cond(
|
||||
|
||||
/// Parses a `let $pat = $expr` pseudo-expression.
|
||||
fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, Box<Expr>> {
|
||||
let recovered = if !restrictions.contains(Restrictions::ALLOW_LET) {
|
||||
let recovered: Recovered = if !restrictions.contains(Restrictions::ALLOW_LET) {
|
||||
let err = errors::ExpectedExpressionFoundLet {
|
||||
span: self.token.span,
|
||||
reason: errors::ForbiddenLetReason::OtherForbidden,
|
||||
@@ -3463,20 +3463,54 @@ pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<Box<Expr>>> {
|
||||
pub(crate) fn eat_metavar_guard(&mut self) -> Option<Box<Guard>> {
|
||||
self.eat_metavar_seq_with_matcher(
|
||||
|mv_kind| matches!(mv_kind, MetaVarKind::Guard),
|
||||
|this| this.parse_match_arm_guard(),
|
||||
)
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<Box<Guard>>> {
|
||||
if let Some(guard) = self.eat_metavar_guard() {
|
||||
return Ok(Some(guard));
|
||||
}
|
||||
|
||||
if !self.eat_keyword(exp!(If)) {
|
||||
// No match arm guard present.
|
||||
return Ok(None);
|
||||
}
|
||||
self.expect_match_arm_guard_cond(ForceCollect::No).map(Some)
|
||||
}
|
||||
|
||||
let mut cond = self.parse_match_guard_condition()?;
|
||||
pub(crate) fn expect_match_arm_guard(
|
||||
&mut self,
|
||||
force_collect: ForceCollect,
|
||||
) -> PResult<'a, Box<Guard>> {
|
||||
if let Some(guard) = self.eat_metavar_guard() {
|
||||
return Ok(guard);
|
||||
}
|
||||
|
||||
self.expect_keyword(exp!(If))?;
|
||||
self.expect_match_arm_guard_cond(force_collect)
|
||||
}
|
||||
|
||||
fn expect_match_arm_guard_cond(
|
||||
&mut self,
|
||||
force_collect: ForceCollect,
|
||||
) -> PResult<'a, Box<Guard>> {
|
||||
let leading_if_span = self.prev_token.span;
|
||||
|
||||
let mut cond = self.parse_match_guard_condition(force_collect)?;
|
||||
let cond_span = cond.span;
|
||||
|
||||
CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
|
||||
|
||||
Ok(Some(cond))
|
||||
let guard = Guard { cond: *cond, span_with_leading_if: leading_if_span.to(cond_span) };
|
||||
Ok(Box::new(guard))
|
||||
}
|
||||
|
||||
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Expr>>)> {
|
||||
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Guard>>)> {
|
||||
if self.token == token::OpenParen {
|
||||
let left = self.token.span;
|
||||
let pat = self.parse_pat_no_top_guard(
|
||||
@@ -3492,10 +3526,10 @@ fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Expr
|
||||
// FIXME(guard_patterns): convert this to a normal guard instead
|
||||
let span = pat.span;
|
||||
let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() };
|
||||
let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() };
|
||||
self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
|
||||
let ast::PatKind::Guard(_, mut guard) = subpat.kind else { unreachable!() };
|
||||
self.psess.gated_spans.ungate_last(sym::guard_patterns, guard.span());
|
||||
let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
|
||||
checker.visit_expr(&mut cond);
|
||||
checker.visit_expr(&mut guard.cond);
|
||||
|
||||
let right = self.prev_token.span;
|
||||
self.dcx().emit_err(errors::ParenthesesInMatchPat {
|
||||
@@ -3503,14 +3537,10 @@ fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Expr
|
||||
sugg: errors::ParenthesesInMatchPatSugg { left, right },
|
||||
});
|
||||
|
||||
Ok((
|
||||
self.mk_pat(span, ast::PatKind::Wild),
|
||||
(if let Some(guar) = checker.found_incorrect_let_chain {
|
||||
Some(self.mk_expr_err(cond.span, guar))
|
||||
} else {
|
||||
Some(cond)
|
||||
}),
|
||||
))
|
||||
if let Some(guar) = checker.found_incorrect_let_chain {
|
||||
guard.cond = *self.mk_expr_err(guard.span(), guar);
|
||||
}
|
||||
Ok((self.mk_pat(span, ast::PatKind::Wild), Some(guard)))
|
||||
} else {
|
||||
Ok((pat, self.parse_match_arm_guard()?))
|
||||
}
|
||||
@@ -3526,33 +3556,47 @@ fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Expr
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_match_guard_condition(&mut self) -> PResult<'a, Box<Expr>> {
|
||||
fn parse_match_guard_condition(
|
||||
&mut self,
|
||||
force_collect: ForceCollect,
|
||||
) -> PResult<'a, Box<Expr>> {
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
match self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) {
|
||||
Ok((expr, _)) => Ok(expr),
|
||||
Err(mut err) => {
|
||||
if self.prev_token == token::OpenBrace {
|
||||
let sugg_sp = self.prev_token.span.shrink_to_lo();
|
||||
// Consume everything within the braces, let's avoid further parse
|
||||
// errors.
|
||||
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
|
||||
let msg = "you might have meant to start a match arm after the match guard";
|
||||
if self.eat(exp!(CloseBrace)) {
|
||||
let applicability = if self.token != token::FatArrow {
|
||||
// We have high confidence that we indeed didn't have a struct
|
||||
// literal in the match guard, but rather we had some operation
|
||||
// that ended in a path, immediately followed by a block that was
|
||||
// meant to be the match arm.
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
err.span_suggestion_verbose(sugg_sp, msg, "=> ", applicability);
|
||||
let expr = self.collect_tokens(
|
||||
None,
|
||||
AttrWrapper::empty(),
|
||||
force_collect,
|
||||
|this, _empty_attrs| {
|
||||
match this
|
||||
.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs)
|
||||
{
|
||||
Ok((expr, _)) => Ok((expr, Trailing::No, UsePreAttrPos::No)),
|
||||
Err(mut err) => {
|
||||
if this.prev_token == token::OpenBrace {
|
||||
let sugg_sp = this.prev_token.span.shrink_to_lo();
|
||||
// Consume everything within the braces, let's avoid further parse
|
||||
// errors.
|
||||
this.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
|
||||
let msg =
|
||||
"you might have meant to start a match arm after the match guard";
|
||||
if this.eat(exp!(CloseBrace)) {
|
||||
let applicability = if this.token != token::FatArrow {
|
||||
// We have high confidence that we indeed didn't have a struct
|
||||
// literal in the match guard, but rather we had some operation
|
||||
// that ended in a path, immediately followed by a block that was
|
||||
// meant to be the match arm.
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
err.span_suggestion_verbose(sugg_sp, msg, "=> ", applicability);
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
)?;
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
pub(crate) fn is_builtin(&self) -> bool {
|
||||
|
||||
@@ -1788,4 +1788,5 @@ pub enum ParseNtResult {
|
||||
Meta(Box<ast::AttrItem>),
|
||||
Path(Box<ast::Path>),
|
||||
Vis(Box<ast::Visibility>),
|
||||
Guard(Box<ast::Guard>),
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ fn may_be_ident(kind: MetaVarKind) -> bool {
|
||||
|
||||
MetaVarKind::Item
|
||||
| MetaVarKind::Block
|
||||
| MetaVarKind::Vis => false,
|
||||
| MetaVarKind::Vis
|
||||
| MetaVarKind::Guard => false,
|
||||
|
||||
MetaVarKind::Ident
|
||||
| MetaVarKind::Lifetime
|
||||
@@ -86,7 +87,8 @@ fn may_be_ident(kind: MetaVarKind) -> bool {
|
||||
| MetaVarKind::Ty { .. }
|
||||
| MetaVarKind::Meta { .. }
|
||||
| MetaVarKind::Path
|
||||
| MetaVarKind::Vis => false,
|
||||
| MetaVarKind::Vis
|
||||
| MetaVarKind::Guard => false,
|
||||
MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -103,6 +105,7 @@ fn may_be_ident(kind: MetaVarKind) -> bool {
|
||||
token::Lifetime(..) | token::NtLifetime(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
NonterminalKind::Guard => token.is_keyword(kw::If),
|
||||
NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
|
||||
token.kind.close_delim().is_none()
|
||||
}
|
||||
@@ -196,6 +199,9 @@ pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseN
|
||||
}))
|
||||
}
|
||||
}
|
||||
NonterminalKind::Guard => {
|
||||
Ok(ParseNtResult::Guard(self.expect_match_arm_guard(ForceCollect::Yes)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::{
|
||||
self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
|
||||
Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
|
||||
self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, Guard, LocalKind, MacCall,
|
||||
Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt,
|
||||
StmtKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
|
||||
@@ -110,11 +111,19 @@ pub fn parse_pat_allow_top_guard(
|
||||
let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?;
|
||||
|
||||
if self.eat_keyword(exp!(If)) {
|
||||
let cond = self.parse_expr()?;
|
||||
let guard = if let Some(guard) = self.eat_metavar_guard() {
|
||||
guard
|
||||
} else {
|
||||
let leading_if_span = self.prev_token.span;
|
||||
let cond = self.parse_expr()?;
|
||||
let cond_span = cond.span;
|
||||
Box::new(Guard { cond: *cond, span_with_leading_if: leading_if_span.to(cond_span) })
|
||||
};
|
||||
|
||||
// Feature-gate guard patterns
|
||||
self.psess.gated_spans.gate(sym::guard_patterns, cond.span);
|
||||
let span = pat.span.to(cond.span);
|
||||
Ok(self.mk_pat(span, PatKind::Guard(Box::new(pat), cond)))
|
||||
self.psess.gated_spans.gate(sym::guard_patterns, guard.span());
|
||||
let span = pat.span.to(guard.span());
|
||||
Ok(self.mk_pat(span, PatKind::Guard(Box::new(pat), guard)))
|
||||
} else {
|
||||
Ok(pat)
|
||||
}
|
||||
@@ -601,17 +610,18 @@ fn maybe_add_suggestions_then_emit(
|
||||
}
|
||||
Some(guard) => {
|
||||
// Are parentheses required around the old guard?
|
||||
let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd;
|
||||
let wrap_guard =
|
||||
guard.cond.precedence() <= ExprPrecedence::LAnd;
|
||||
|
||||
err.subdiagnostic(
|
||||
UnexpectedExpressionInPatternSugg::UpdateGuard {
|
||||
ident_span,
|
||||
guard_lo: if wrap_guard {
|
||||
Some(guard.span.shrink_to_lo())
|
||||
Some(guard.span().shrink_to_lo())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
guard_hi: guard.span.shrink_to_hi(),
|
||||
guard_hi: guard.span().shrink_to_hi(),
|
||||
guard_hi_paren: if wrap_guard { ")" } else { "" },
|
||||
ident,
|
||||
expr,
|
||||
|
||||
@@ -4073,7 +4073,7 @@ fn check_consistent_bindings(&mut self, pat: &'ast Pat) {
|
||||
fn resolve_arm(&mut self, arm: &'ast Arm) {
|
||||
self.with_rib(ValueNS, RibKind::Normal, |this| {
|
||||
this.resolve_pattern_top(&arm.pat, PatternSource::Match);
|
||||
visit_opt!(this, visit_expr, &arm.guard);
|
||||
visit_opt!(this, visit_expr, arm.guard.as_ref().map(|g| &g.cond));
|
||||
visit_opt!(this, visit_expr, &arm.body);
|
||||
});
|
||||
}
|
||||
@@ -4215,7 +4215,7 @@ fn resolve_pattern_inner(
|
||||
let subpat_bindings = bindings.pop().unwrap().1;
|
||||
self.with_rib(ValueNS, RibKind::Normal, |this| {
|
||||
*this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone();
|
||||
this.resolve_expr(guard, None);
|
||||
this.resolve_expr(&guard.cond, None);
|
||||
});
|
||||
// Propagate the subpattern's bindings upwards.
|
||||
// FIXME(guard_patterns): For `if let` guards, we'll also need to get the
|
||||
|
||||
@@ -547,6 +547,13 @@ fn name(&self) -> Option<Symbol> {
|
||||
ModuleKind::Def(.., name) => name,
|
||||
}
|
||||
}
|
||||
|
||||
fn opt_def_id(&self) -> Option<DefId> {
|
||||
match self {
|
||||
ModuleKind::Def(_, def_id, _) => Some(*def_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Combination of a symbol and its macros 2.0 normalized hygiene context.
|
||||
@@ -784,10 +791,7 @@ fn def_id(self) -> DefId {
|
||||
}
|
||||
|
||||
fn opt_def_id(self) -> Option<DefId> {
|
||||
match self.kind {
|
||||
ModuleKind::Def(_, def_id, _) => Some(def_id),
|
||||
_ => None,
|
||||
}
|
||||
self.kind.opt_def_id()
|
||||
}
|
||||
|
||||
// `self` resolves to the first module ancestor that `is_normal`.
|
||||
@@ -1450,14 +1454,19 @@ fn new_module(
|
||||
&'ra self,
|
||||
parent: Option<Module<'ra>>,
|
||||
kind: ModuleKind,
|
||||
vis: Visibility<DefId>,
|
||||
expn_id: ExpnId,
|
||||
span: Span,
|
||||
no_implicit_prelude: bool,
|
||||
) -> Module<'ra> {
|
||||
let self_decl = match kind {
|
||||
ModuleKind::Def(def_kind, def_id, _) => {
|
||||
Some(self.new_pub_def_decl(Res::Def(def_kind, def_id), span, LocalExpnId::ROOT))
|
||||
}
|
||||
ModuleKind::Def(def_kind, def_id, _) => Some(self.new_def_decl(
|
||||
Res::Def(def_kind, def_id),
|
||||
vis,
|
||||
span,
|
||||
LocalExpnId::ROOT,
|
||||
None,
|
||||
)),
|
||||
ModuleKind::Block => None,
|
||||
};
|
||||
Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new(
|
||||
@@ -1639,6 +1648,7 @@ pub fn new(
|
||||
let graph_root = arenas.new_module(
|
||||
None,
|
||||
ModuleKind::Def(DefKind::Mod, root_def_id, None),
|
||||
Visibility::Public,
|
||||
ExpnId::root(),
|
||||
crate_span,
|
||||
attr::contains_name(attrs, sym::no_implicit_prelude),
|
||||
@@ -1648,6 +1658,7 @@ pub fn new(
|
||||
let empty_module = arenas.new_module(
|
||||
None,
|
||||
ModuleKind::Def(DefKind::Mod, root_def_id, None),
|
||||
Visibility::Public,
|
||||
ExpnId::root(),
|
||||
DUMMY_SP,
|
||||
true,
|
||||
@@ -1749,7 +1760,9 @@ fn new_local_module(
|
||||
span: Span,
|
||||
no_implicit_prelude: bool,
|
||||
) -> Module<'ra> {
|
||||
let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude);
|
||||
let vis =
|
||||
kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id));
|
||||
let module = self.arenas.new_module(parent, kind, vis, expn_id, span, no_implicit_prelude);
|
||||
self.local_modules.push(module);
|
||||
if let Some(def_id) = module.opt_def_id() {
|
||||
self.local_module_map.insert(def_id.expect_local(), module);
|
||||
@@ -1765,7 +1778,9 @@ fn new_extern_module(
|
||||
span: Span,
|
||||
no_implicit_prelude: bool,
|
||||
) -> Module<'ra> {
|
||||
let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude);
|
||||
let vis =
|
||||
kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id));
|
||||
let module = self.arenas.new_module(parent, kind, vis, expn_id, span, no_implicit_prelude);
|
||||
self.extern_module_map.borrow_mut().insert(module.def_id(), module);
|
||||
module
|
||||
}
|
||||
|
||||
@@ -229,6 +229,10 @@ macro_rules! ins_sym {
|
||||
if s == SanitizerSet::KERNELADDRESS {
|
||||
s = SanitizerSet::ADDRESS;
|
||||
}
|
||||
// KHWASAN is still HWASAN under the hood, so it uses the same attribute.
|
||||
if s == SanitizerSet::KERNELHWADDRESS {
|
||||
s = SanitizerSet::HWADDRESS;
|
||||
}
|
||||
ins_str!(sym::sanitize, &s.to_string());
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ pub(super) fn sanitizer(l: &TargetModifier, r: Option<&TargetModifier>) -> bool
|
||||
| SanitizerSet::SHADOWCALLSTACK
|
||||
| SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS
|
||||
| SanitizerSet::SAFESTACK
|
||||
| SanitizerSet::DATAFLOW;
|
||||
|
||||
@@ -817,7 +818,7 @@ mod desc {
|
||||
pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)";
|
||||
pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy;
|
||||
pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||
pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'";
|
||||
pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `kernel-hwaddress`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'";
|
||||
pub(crate) const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
|
||||
pub(crate) const parse_cfguard: &str =
|
||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
||||
@@ -1265,6 +1266,7 @@ pub(crate) fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool
|
||||
"dataflow" => SanitizerSet::DATAFLOW,
|
||||
"kcfi" => SanitizerSet::KCFI,
|
||||
"kernel-address" => SanitizerSet::KERNELADDRESS,
|
||||
"kernel-hwaddress" => SanitizerSet::KERNELHWADDRESS,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"memtag" => SanitizerSet::MEMTAG,
|
||||
|
||||
@@ -533,9 +533,12 @@ pub fn needs_plt(&self) -> bool {
|
||||
pub fn emit_lifetime_markers(&self) -> bool {
|
||||
self.opts.optimize != config::OptLevel::No
|
||||
// AddressSanitizer and KernelAddressSanitizer uses lifetimes to detect use after scope bugs.
|
||||
//
|
||||
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|
||||
// HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
|
||||
|| self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
|
||||
//
|
||||
// HWAddressSanitizer and KernelHWAddressSanitizer will use lifetimes to detect use after
|
||||
// scope bugs in the future.
|
||||
|| self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
|
||||
}
|
||||
|
||||
pub fn diagnostic_width(&self) -> usize {
|
||||
|
||||
@@ -1028,6 +1028,7 @@
|
||||
global_registration,
|
||||
globs,
|
||||
gt,
|
||||
guard,
|
||||
guard_patterns,
|
||||
half_open_range_patterns,
|
||||
half_open_range_patterns_in_slices,
|
||||
@@ -1116,6 +1117,7 @@
|
||||
iterator_collect_fn,
|
||||
kcfi,
|
||||
kernel_address,
|
||||
kernel_hwaddress,
|
||||
keylocker_x86,
|
||||
keyword,
|
||||
kind,
|
||||
@@ -1192,6 +1194,7 @@
|
||||
macro_derive,
|
||||
macro_escape,
|
||||
macro_export,
|
||||
macro_guard_matcher,
|
||||
macro_lifetime_matcher,
|
||||
macro_literal_matcher,
|
||||
macro_metavar_expr,
|
||||
|
||||
@@ -1175,9 +1175,10 @@ impl SanitizerSet: u16 {
|
||||
const SHADOWCALLSTACK = 1 << 7;
|
||||
const KCFI = 1 << 8;
|
||||
const KERNELADDRESS = 1 << 9;
|
||||
const SAFESTACK = 1 << 10;
|
||||
const DATAFLOW = 1 << 11;
|
||||
const REALTIME = 1 << 12;
|
||||
const KERNELHWADDRESS = 1 << 10;
|
||||
const SAFESTACK = 1 << 11;
|
||||
const DATAFLOW = 1 << 12;
|
||||
const REALTIME = 1 << 13;
|
||||
}
|
||||
}
|
||||
rustc_data_structures::external_bitflags_debug! { SanitizerSet }
|
||||
@@ -1191,24 +1192,32 @@ impl SanitizerSet {
|
||||
(SanitizerSet::ADDRESS, SanitizerSet::HWADDRESS),
|
||||
(SanitizerSet::ADDRESS, SanitizerSet::MEMTAG),
|
||||
(SanitizerSet::ADDRESS, SanitizerSet::KERNELADDRESS),
|
||||
(SanitizerSet::ADDRESS, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::ADDRESS, SanitizerSet::SAFESTACK),
|
||||
(SanitizerSet::LEAK, SanitizerSet::MEMORY),
|
||||
(SanitizerSet::LEAK, SanitizerSet::THREAD),
|
||||
(SanitizerSet::LEAK, SanitizerSet::KERNELADDRESS),
|
||||
(SanitizerSet::LEAK, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::LEAK, SanitizerSet::SAFESTACK),
|
||||
(SanitizerSet::MEMORY, SanitizerSet::THREAD),
|
||||
(SanitizerSet::MEMORY, SanitizerSet::HWADDRESS),
|
||||
(SanitizerSet::MEMORY, SanitizerSet::KERNELADDRESS),
|
||||
(SanitizerSet::MEMORY, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::MEMORY, SanitizerSet::SAFESTACK),
|
||||
(SanitizerSet::THREAD, SanitizerSet::HWADDRESS),
|
||||
(SanitizerSet::THREAD, SanitizerSet::KERNELADDRESS),
|
||||
(SanitizerSet::THREAD, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::THREAD, SanitizerSet::SAFESTACK),
|
||||
(SanitizerSet::HWADDRESS, SanitizerSet::MEMTAG),
|
||||
(SanitizerSet::HWADDRESS, SanitizerSet::KERNELADDRESS),
|
||||
(SanitizerSet::HWADDRESS, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::HWADDRESS, SanitizerSet::SAFESTACK),
|
||||
(SanitizerSet::CFI, SanitizerSet::KCFI),
|
||||
(SanitizerSet::MEMTAG, SanitizerSet::KERNELADDRESS),
|
||||
(SanitizerSet::MEMTAG, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::KERNELADDRESS, SanitizerSet::KERNELHWADDRESS),
|
||||
(SanitizerSet::KERNELADDRESS, SanitizerSet::SAFESTACK),
|
||||
(SanitizerSet::KERNELHWADDRESS, SanitizerSet::SAFESTACK),
|
||||
];
|
||||
|
||||
/// Return sanitizer's name
|
||||
@@ -1221,6 +1230,7 @@ pub fn as_str(self) -> Option<&'static str> {
|
||||
SanitizerSet::DATAFLOW => "dataflow",
|
||||
SanitizerSet::KCFI => "kcfi",
|
||||
SanitizerSet::KERNELADDRESS => "kernel-address",
|
||||
SanitizerSet::KERNELHWADDRESS => "kernel-hwaddress",
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::MEMTAG => "memtag",
|
||||
@@ -1266,6 +1276,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
"dataflow" => SanitizerSet::DATAFLOW,
|
||||
"kcfi" => SanitizerSet::KCFI,
|
||||
"kernel-address" => SanitizerSet::KERNELADDRESS,
|
||||
"kernel-hwaddress" => SanitizerSet::KERNELHWADDRESS,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"memtag" => SanitizerSet::MEMTAG,
|
||||
|
||||
@@ -22,7 +22,9 @@ pub(crate) fn target() -> Target {
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
|
||||
supported_sanitizers: SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS,
|
||||
stack_probes: StackProbeType::Inline,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
endian: Endian::Big,
|
||||
|
||||
@@ -21,7 +21,9 @@ pub(crate) fn target() -> Target {
|
||||
&["--fix-cortex-a53-843419"],
|
||||
),
|
||||
features: "+v8a,+strict-align,+neon".into(),
|
||||
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
|
||||
supported_sanitizers: SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS,
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
|
||||
@@ -21,7 +21,9 @@ pub(crate) fn target() -> Target {
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
|
||||
supported_sanitizers: SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS,
|
||||
stack_probes: StackProbeType::Inline,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
default_uwtable: true,
|
||||
|
||||
@@ -21,7 +21,9 @@ pub(crate) fn target() -> Target {
|
||||
&["--fix-cortex-a53-843419"],
|
||||
),
|
||||
features: "+v8a,+strict-align,+neon".into(),
|
||||
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
|
||||
supported_sanitizers: SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS,
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
|
||||
@@ -8,7 +8,9 @@ pub(crate) fn target() -> Target {
|
||||
// based off the aarch64-unknown-none target at time of addition
|
||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||
linker: Some("rust-lld".into()),
|
||||
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
|
||||
supported_sanitizers: SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS,
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
|
||||
@@ -12,7 +12,9 @@ pub(crate) fn target() -> Target {
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
|
||||
supported_sanitizers: SanitizerSet::KCFI
|
||||
| SanitizerSet::KERNELADDRESS
|
||||
| SanitizerSet::KERNELHWADDRESS,
|
||||
stack_probes: StackProbeType::Inline,
|
||||
panic_strategy: PanicStrategy::Abort,
|
||||
default_uwtable: true,
|
||||
|
||||
@@ -1212,7 +1212,7 @@ pub fn to_lowercase(self) -> ToLowercase {
|
||||
/// returned by [`Self::to_uppercase`]. Prefer this method when seeking to capitalize
|
||||
/// Only The First Letter of a word, but use [`Self::to_uppercase`] for ALL CAPS.
|
||||
///
|
||||
/// If this `char` does not have an titlecase mapping, the iterator yields the same `char`.
|
||||
/// If this `char` does not have a titlecase mapping, the iterator yields the same `char`.
|
||||
///
|
||||
/// If this `char` has a one-to-one titlecase mapping given by the [Unicode Character
|
||||
/// Database][ucd] [`UnicodeData.txt`], the iterator yields that `char`.
|
||||
|
||||
@@ -138,8 +138,9 @@ pub const fn dangling() -> Self {
|
||||
///
|
||||
/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
|
||||
#[stable(feature = "nonnull_provenance", since = "1.89.0")]
|
||||
#[rustc_const_unstable(feature = "const_nonnull_with_exposed_provenance", issue = "154215")]
|
||||
#[inline]
|
||||
pub fn with_exposed_provenance(addr: NonZero<usize>) -> Self {
|
||||
pub const fn with_exposed_provenance(addr: NonZero<usize>) -> Self {
|
||||
// SAFETY: we know `addr` is non-zero.
|
||||
unsafe {
|
||||
let ptr = crate::ptr::with_exposed_provenance_mut(addr.get());
|
||||
|
||||
@@ -509,7 +509,7 @@ auto:
|
||||
- name: dist-aarch64-apple
|
||||
env:
|
||||
SCRIPT: >-
|
||||
./x.py dist bootstrap
|
||||
./x.py dist bootstrap enzyme
|
||||
--include-default-paths
|
||||
--host=aarch64-apple-darwin
|
||||
--target=aarch64-apple-darwin
|
||||
|
||||
@@ -22,6 +22,8 @@ This feature allows for use of one of following sanitizers:
|
||||
* [AddressSanitizer](#addresssanitizer) a fast memory error detector.
|
||||
* [HWAddressSanitizer](#hwaddresssanitizer) a memory error detector similar to
|
||||
AddressSanitizer, but based on partial hardware assistance.
|
||||
* [KernelHWAddressSanitizer](#kernelhwaddresssanitizer) variant of
|
||||
HWAddressSanitizer that is designed for bare metal environments.
|
||||
* [LeakSanitizer](#leaksanitizer) a run-time memory leak detector.
|
||||
* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
|
||||
* [RealtimeSanitizer](#realtimesanitizer) a detector of calls to function with
|
||||
@@ -622,6 +624,16 @@ Registers where the failure occurred (pc 0xaaaae0ae4a98):
|
||||
SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94)
|
||||
```
|
||||
|
||||
# KernelHWAddressSanitizer
|
||||
|
||||
KernelHWAddressSanitizer is the kernel version of [HWAddressSanitizer](#hwaddresssanitizer),
|
||||
which achieves the same purpose but is designed for bare-metal environments.
|
||||
|
||||
HWAddressSanitizer is supported on the `aarch64*-unknown-none` and
|
||||
`aarch64*-unknown-none-softfloat` targets.
|
||||
|
||||
See the [Clang HWAddressSanitizer documentation][clang-hwasan] for more details.
|
||||
|
||||
# KernelControlFlowIntegrity
|
||||
|
||||
The LLVM Kernel Control Flow Integrity (CFI) support to the Rust compiler
|
||||
|
||||
+1
-1
Submodule src/llvm-project updated: 41f177ed26...0591836336
@@ -280,7 +280,7 @@ pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& eq_expr_opt(l.body.as_deref(), r.body.as_deref())
|
||||
&& eq_expr_opt(l.guard.as_deref(), r.guard.as_deref())
|
||||
&& eq_expr_opt(l.guard.as_deref().map(|g| &g.cond), r.guard.as_deref().map(|g| &g.cond))
|
||||
&& over(&l.attrs, &r.attrs, eq_attr)
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,7 @@ pub enum Sanitizer {
|
||||
Dataflow,
|
||||
Kcfi,
|
||||
KernelAddress,
|
||||
KernelHwaddress,
|
||||
Leak,
|
||||
Memory,
|
||||
Memtag,
|
||||
|
||||
@@ -179,6 +179,7 @@
|
||||
"needs-sanitizer-hwaddress",
|
||||
"needs-sanitizer-kasan",
|
||||
"needs-sanitizer-kcfi",
|
||||
"needs-sanitizer-khwasan",
|
||||
"needs-sanitizer-leak",
|
||||
"needs-sanitizer-memory",
|
||||
"needs-sanitizer-memtag",
|
||||
|
||||
@@ -46,6 +46,11 @@ pub(super) fn handle_needs(
|
||||
condition: cache.sanitizer_kasan,
|
||||
ignore_reason: "ignored on targets without kernel address sanitizer",
|
||||
},
|
||||
Need {
|
||||
name: "needs-sanitizer-khwasan",
|
||||
condition: cache.sanitizer_khwasan,
|
||||
ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer",
|
||||
},
|
||||
Need {
|
||||
name: "needs-sanitizer-leak",
|
||||
condition: cache.sanitizer_leak,
|
||||
@@ -332,6 +337,7 @@ pub(super) struct CachedNeedsConditions {
|
||||
sanitizer_dataflow: bool,
|
||||
sanitizer_kcfi: bool,
|
||||
sanitizer_kasan: bool,
|
||||
sanitizer_khwasan: bool,
|
||||
sanitizer_leak: bool,
|
||||
sanitizer_memory: bool,
|
||||
sanitizer_thread: bool,
|
||||
@@ -359,6 +365,7 @@ pub(super) fn load(config: &Config) -> Self {
|
||||
sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow),
|
||||
sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi),
|
||||
sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress),
|
||||
sanitizer_khwasan: sanitizers.contains(&Sanitizer::KernelHwaddress),
|
||||
sanitizer_leak: sanitizers.contains(&Sanitizer::Leak),
|
||||
sanitizer_memory: sanitizers.contains(&Sanitizer::Memory),
|
||||
sanitizer_thread: sanitizers.contains(&Sanitizer::Thread),
|
||||
|
||||
@@ -476,8 +476,9 @@ to Miri failing to detect cases of undefined behavior in a program.
|
||||
but reports to the program that it did actually write. This is useful when you
|
||||
are not interested in the actual program's output, but only want to see Miri's
|
||||
errors and warnings.
|
||||
* `-Zmiri-recursive-validation` is a *highly experimental* flag that makes validity checking
|
||||
recurse below references.
|
||||
* `-Zmiri-recursive-validation` is a *highly experimental* flag that makes validity checking recurse
|
||||
*one level* below references. The in-memory value is treated as-if it was inside a
|
||||
`MaybeDangling`, i.e., nested references do not even have to be dereferenceable.
|
||||
* `-Zmiri-preemption-rate` configures the probability that at the end of a basic block, the active
|
||||
thread will be preempted. The default is `0.01` (i.e., 1%). Setting this to `0` disables
|
||||
preemption. Note that even without preemption, the schedule is still non-deterministic:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use std::{mem, ptr};
|
||||
|
||||
extern "C" fn thread_start() -> *mut libc::c_void {
|
||||
//~^ERROR: calling a function with more arguments than it expected
|
||||
panic!()
|
||||
}
|
||||
|
||||
@@ -16,7 +17,6 @@ fn main() {
|
||||
mem::transmute(thread_start);
|
||||
assert_eq!(
|
||||
libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
//~^ERROR: calling a function with more arguments than it expected
|
||||
0
|
||||
);
|
||||
assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
error: Undefined Behavior: calling a function with more arguments than it expected
|
||||
--> tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
LL | extern "C" fn thread_start() -> *mut libc::c_void {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this is on thread `unnamed-ID`
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
note: the current function got called indirectly due to this code
|
||||
--> tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use std::{mem, ptr};
|
||||
|
||||
extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void {
|
||||
//~^ERROR: calling a function with fewer arguments than it requires
|
||||
panic!()
|
||||
}
|
||||
|
||||
@@ -16,7 +17,6 @@ fn main() {
|
||||
mem::transmute(thread_start);
|
||||
assert_eq!(
|
||||
libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
//~^ERROR: calling a function with fewer arguments than it requires
|
||||
0
|
||||
);
|
||||
assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
error: Undefined Behavior: calling a function with fewer arguments than it requires
|
||||
--> tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
LL | extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void {
|
||||
| ^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this is on thread `unnamed-ID`
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
note: the current function got called indirectly due to this code
|
||||
--> tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ fn main() {
|
||||
// This specifically uses a type with scalar representation to tempt Miri to use the
|
||||
// efficient way of storing local variables (outside adressable memory).
|
||||
Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||
//~[stack]^ ERROR: not granting access
|
||||
//~[tree]| ERROR: /read access .* forbidden/
|
||||
}
|
||||
after_call = {
|
||||
Return()
|
||||
@@ -32,6 +30,8 @@ fn main() {
|
||||
|
||||
#[expect(unused_variables, unused_assignments)]
|
||||
fn callee(x: S, mut y: S) {
|
||||
//~[stack]^ ERROR: not granting access
|
||||
//~[tree]| ERROR: /read access .* forbidden/
|
||||
// With the setup above, if `x` and `y` are both moved,
|
||||
// then writing to `y` will change the value stored in `x`!
|
||||
y.0 = 0;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
|
||||
LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn callee(x: S, mut y: S) {
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||
@@ -14,8 +14,13 @@ LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(a
|
||||
help: <TAG> is this argument
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
|
||||
LL | y.0 = 0;
|
||||
| ^^^^^^^
|
||||
LL | fn callee(x: S, mut y: S) {
|
||||
| ^
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: read access through <TAG> (root of the allocation) at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
|
||||
LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn callee(x: S, mut y: S) {
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
@@ -17,14 +17,19 @@ LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(a
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
|
||||
LL | y.0 = 0;
|
||||
| ^^^^^^^
|
||||
LL | fn callee(x: S, mut y: S) {
|
||||
| ^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
|
||||
LL | y.0 = 0;
|
||||
| ^^^^^^^
|
||||
LL | fn callee(x: S, mut y: S) {
|
||||
| ^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ fn main() {
|
||||
// This specifically uses a type with scalar representation to tempt Miri to use the
|
||||
// efficient way of storing local variables (outside adressable memory).
|
||||
Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||
//~[stack]^ ERROR: not granting access
|
||||
//~[tree]| ERROR: /reborrow .* forbidden/
|
||||
}
|
||||
after_call = {
|
||||
Return()
|
||||
@@ -30,5 +28,7 @@ fn main() {
|
||||
}
|
||||
|
||||
fn callee(x: S) -> S {
|
||||
//~[stack]^ ERROR: not granting access
|
||||
//~[tree]| ERROR: /reborrow .* forbidden/
|
||||
x
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
|
||||
LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn callee(x: S) -> S {
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||
@@ -14,8 +14,13 @@ LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call),
|
||||
help: <TAG> is this argument
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
|
||||
LL | x
|
||||
| ^
|
||||
LL | fn callee(x: S) -> S {
|
||||
| ^
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: reborrow through <TAG> (root of the allocation) at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
|
||||
LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn callee(x: S) -> S {
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
@@ -17,14 +17,19 @@ LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call),
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
|
||||
LL | x
|
||||
| ^
|
||||
LL | fn callee(x: S) -> S {
|
||||
| ^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
|
||||
LL | x
|
||||
| ^
|
||||
LL | fn callee(x: S) -> S {
|
||||
| ^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ LL | | }
|
||||
help: <TAG> is this argument
|
||||
--> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(S(0)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn callee(x: S, ptr: *mut S) {
|
||||
| ^
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
at tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
|
||||
|
||||
@@ -22,13 +22,13 @@ LL | | }
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(S(0)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn callee(x: S, ptr: *mut S) {
|
||||
| ^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(S(0)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn callee(x: S, ptr: *mut S) {
|
||||
| ^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
|
||||
@@ -19,8 +19,8 @@ LL | | }
|
||||
help: <TAG> is this argument
|
||||
--> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
|
||||
|
|
||||
LL | x.0 = 0;
|
||||
| ^^^^^^^
|
||||
LL | fn change_arg(mut x: S, ptr: *mut S) {
|
||||
| ^^^^^
|
||||
= note: stack backtrace:
|
||||
0: change_arg
|
||||
at tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
|
||||
|
||||
@@ -22,13 +22,13 @@ LL | | }
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
|
||||
|
|
||||
LL | x.0 = 0;
|
||||
| ^^^^^^^
|
||||
LL | fn change_arg(mut x: S, ptr: *mut S) {
|
||||
| ^^^^^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
|
||||
|
|
||||
LL | x.0 = 0;
|
||||
| ^^^^^^^
|
||||
LL | fn change_arg(mut x: S, ptr: *mut S) {
|
||||
| ^^^^^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: change_arg
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#[no_mangle]
|
||||
fn foo() {}
|
||||
fn foo() {} //~ ERROR: calling a function with more arguments than it expected
|
||||
|
||||
fn main() {
|
||||
extern "Rust" {
|
||||
fn foo(_: i32);
|
||||
}
|
||||
unsafe { foo(1) } //~ ERROR: calling a function with more arguments than it expected
|
||||
unsafe { foo(1) }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
error: Undefined Behavior: calling a function with more arguments than it expected
|
||||
--> tests/fail/function_calls/exported_symbol_wrong_arguments.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { foo(1) }
|
||||
| ^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn foo() {}
|
||||
| ^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: stack backtrace:
|
||||
0: foo
|
||||
at tests/fail/function_calls/exported_symbol_wrong_arguments.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_calls/exported_symbol_wrong_arguments.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ LL | | }
|
||||
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.read() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
= note: stack backtrace:
|
||||
0: myfun
|
||||
at tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||
|
||||
@@ -22,13 +22,13 @@ LL | | }
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.read() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.read() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: myfun
|
||||
|
||||
@@ -19,8 +19,8 @@ LL | | }
|
||||
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(0) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
= note: stack backtrace:
|
||||
0: myfun
|
||||
at tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||
|
||||
@@ -22,13 +22,13 @@ LL | | }
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(0) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(0) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: myfun
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ LL | | }
|
||||
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||
|
|
||||
LL | become myfun2(ptr)
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
= note: stack backtrace:
|
||||
0: myfun2
|
||||
at tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||
|
||||
+4
-4
@@ -22,13 +22,13 @@ LL | | }
|
||||
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(0) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun2(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { ptr.write(0) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn myfun2(ptr: *mut i32) -> i32 {
|
||||
| ^^^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
= note: stack backtrace:
|
||||
0: myfun2
|
||||
|
||||
@@ -12,10 +12,10 @@ struct S(
|
||||
type A = [i32; 4];
|
||||
|
||||
fn main() {
|
||||
fn f(_: S) {}
|
||||
fn f(_: S) {} //~ ERROR: type S passing argument of type [i32; 4]
|
||||
|
||||
// These two types have the same size but are still not compatible.
|
||||
let g = unsafe { std::mem::transmute::<fn(S), fn(A)>(f) };
|
||||
|
||||
g(Default::default()) //~ ERROR: type S passing argument of type [i32; 4]
|
||||
g(Default::default())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type S passing argument of type [i32; 4]
|
||||
--> tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs:LL:CC
|
||||
|
|
||||
LL | g(Default::default())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn f(_: S) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
fn f(_: f32) {}
|
||||
fn f(_: f32) {} //~ ERROR: type f32 passing argument of type i32
|
||||
|
||||
let g = unsafe { std::mem::transmute::<fn(f32), fn(i32)>(f) };
|
||||
|
||||
g(42) //~ ERROR: type f32 passing argument of type i32
|
||||
g(42)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type f32 passing argument of type i32
|
||||
--> tests/fail/function_pointers/abi_mismatch_int_vs_float.rs:LL:CC
|
||||
|
|
||||
LL | g(42)
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
LL | fn f(_: f32) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_int_vs_float.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_int_vs_float.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
fn f(_: *const [i32]) {}
|
||||
fn f(_: *const [i32]) {} //~ ERROR: type *const [i32] passing argument of type *const i32
|
||||
|
||||
let g = unsafe { std::mem::transmute::<fn(*const [i32]), fn(*const i32)>(f) };
|
||||
|
||||
g(&42 as *const i32) //~ ERROR: type *const [i32] passing argument of type *const i32
|
||||
g(&42 as *const i32)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type *const [i32] passing argument of type *const i32
|
||||
--> tests/fail/function_pointers/abi_mismatch_raw_pointer.rs:LL:CC
|
||||
|
|
||||
LL | g(&42 as *const i32)
|
||||
| ^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn f(_: *const [i32]) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_raw_pointer.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_raw_pointer.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
struct S2(i32);
|
||||
|
||||
fn callee(_s: S2) {}
|
||||
//~^ ERROR: type S2 passing argument of type S1
|
||||
|
||||
fn main() {
|
||||
let fnptr: fn(S2) = callee;
|
||||
let fnptr: fn(S1) = unsafe { std::mem::transmute(fnptr) };
|
||||
fnptr(S1(NonZero::new(1).unwrap()));
|
||||
//~^ ERROR: type S2 passing argument of type S1
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type S2 passing argument of type S1
|
||||
--> tests/fail/function_pointers/abi_mismatch_repr_C.rs:LL:CC
|
||||
|
|
||||
LL | fnptr(S1(NonZero::new(1).unwrap()));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn callee(_s: S2) {}
|
||||
| ^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: callee
|
||||
at tests/fail/function_pointers/abi_mismatch_repr_C.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_repr_C.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
fn main() {
|
||||
fn f() -> u32 {
|
||||
fn f() -> u32 { //~ ERROR: type u32 passing return place of type ()
|
||||
42
|
||||
}
|
||||
|
||||
let g = unsafe { std::mem::transmute::<fn() -> u32, fn()>(f) };
|
||||
|
||||
g() //~ ERROR: type u32 passing return place of type ()
|
||||
g()
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function with return type u32 passing return place of type ()
|
||||
--> tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC
|
||||
|
|
||||
LL | g()
|
||||
| ^^^ Undefined Behavior occurred here
|
||||
LL | fn f() -> u32 {
|
||||
| ^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
fn f(_: (i32, i32)) {}
|
||||
fn f(_: (i32, i32)) {} //~ ERROR: type (i32, i32) passing argument of type i32
|
||||
|
||||
let g = unsafe { std::mem::transmute::<fn((i32, i32)), fn(i32)>(f) };
|
||||
|
||||
g(42) //~ ERROR: type (i32, i32) passing argument of type i32
|
||||
g(42)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type (i32, i32) passing argument of type i32
|
||||
--> tests/fail/function_pointers/abi_mismatch_simple.rs:LL:CC
|
||||
|
|
||||
LL | g(42)
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
LL | fn f(_: (i32, i32)) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_simple.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_simple.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
fn f(_: (i32, i32)) {}
|
||||
fn f(_: (i32, i32)) {} //~ ERROR: calling a function with fewer arguments than it requires
|
||||
|
||||
let g = unsafe { std::mem::transmute::<fn((i32, i32)), fn()>(f) };
|
||||
|
||||
g() //~ ERROR: calling a function with fewer arguments than it requires
|
||||
g()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
error: Undefined Behavior: calling a function with fewer arguments than it requires
|
||||
--> tests/fail/function_pointers/abi_mismatch_too_few_args.rs:LL:CC
|
||||
|
|
||||
LL | g()
|
||||
| ^^^ Undefined Behavior occurred here
|
||||
LL | fn f(_: (i32, i32)) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_too_few_args.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_too_few_args.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
fn f() {}
|
||||
fn f() {} //~ ERROR: calling a function with more arguments than it expected
|
||||
|
||||
let g = unsafe { std::mem::transmute::<fn(), fn(i32)>(f) };
|
||||
|
||||
g(42) //~ ERROR: calling a function with more arguments than it expected
|
||||
g(42)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
error: Undefined Behavior: calling a function with more arguments than it expected
|
||||
--> tests/fail/function_pointers/abi_mismatch_too_many_args.rs:LL:CC
|
||||
|
|
||||
LL | g(42)
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
LL | fn f() {}
|
||||
| ^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_too_many_args.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_too_many_args.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
use std::simd;
|
||||
|
||||
fn main() {
|
||||
fn f(_: simd::u32x8) {}
|
||||
fn f(_: simd::u32x8) {} //~ ERROR: type std::simd::Simd<u32, 8> passing argument of type std::simd::Simd<u64, 4>
|
||||
|
||||
// These two vector types have the same size but are still not compatible.
|
||||
let g = unsafe { std::mem::transmute::<fn(simd::u32x8), fn(simd::u64x4)>(f) };
|
||||
|
||||
g(Default::default()) //~ ERROR: type std::simd::Simd<u32, 8> passing argument of type std::simd::Simd<u64, 4>
|
||||
g(Default::default())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type std::simd::Simd<u32, 8> passing argument of type std::simd::Simd<u64, 4>
|
||||
--> tests/fail/function_pointers/abi_mismatch_vector.rs:LL:CC
|
||||
|
|
||||
LL | g(Default::default())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
LL | fn f(_: simd::u32x8) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: main::f
|
||||
at tests/fail/function_pointers/abi_mismatch_vector.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/function_pointers/abi_mismatch_vector.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
unsafe extern "C" fn ctor() -> i32 {
|
||||
//~^ERROR: calling a function with return type i32 passing return place of type ()
|
||||
0
|
||||
}
|
||||
|
||||
@@ -30,7 +31,6 @@ macro_rules! ctor {
|
||||
)]
|
||||
#[used]
|
||||
static $ident: unsafe extern "C" fn() -> i32 = $ctor;
|
||||
//~^ERROR: calling a function with return type i32 passing return place of type ()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
error: Undefined Behavior: calling a function with return type i32 passing return place of type ()
|
||||
--> tests/fail/shims/ctor_wrong_ret_type.rs:LL:CC
|
||||
|
|
||||
LL | static $ident: unsafe extern "C" fn() -> i32 = $ctor;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
|
||||
...
|
||||
LL | ctor! { CTOR = ctor }
|
||||
| --------------------- in this macro invocation
|
||||
LL | unsafe extern "C" fn ctor() -> i32 {
|
||||
| ^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: this error occurred while pushing a call frame onto an empty stack
|
||||
= note: the span indicates which code caused the function to be called, but may not be the literal call site
|
||||
note: the current function got called indirectly due to this code
|
||||
--> tests/fail/shims/ctor_wrong_ret_type.rs:LL:CC
|
||||
|
|
||||
LL | static $ident: unsafe extern "C" fn() -> i32 = $ctor;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | ctor! { CTOR = ctor }
|
||||
| --------------------- in this macro invocation
|
||||
= note: this error originates in the macro `ctor` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn main() {
|
||||
// FIXME(explicit_tail_calls):
|
||||
// the error should point to `become g(x)`,
|
||||
// but tail calls mess up the backtrace it seems like...
|
||||
f(0);
|
||||
//~^ error: type i32 passing argument of type u32
|
||||
}
|
||||
|
||||
fn f(x: u32) {
|
||||
let g = unsafe { std::mem::transmute::<fn(i32), fn(u32)>(g) };
|
||||
become g(x);
|
||||
become g(x); // FIXME ideally this should also be involved in the error somehow,
|
||||
// but by the time we pass the argument, `f`'s stackframe has already been popped.
|
||||
}
|
||||
|
||||
fn g(_: i32) {}
|
||||
//~^ error: type i32 passing argument of type u32
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: calling a function whose parameter #1 has type i32 passing argument of type u32
|
||||
--> tests/fail/tail_calls/signature-mismatch-arg.rs:LL:CC
|
||||
|
|
||||
LL | f(0);
|
||||
| ^^^^ Undefined Behavior occurred here
|
||||
LL | fn g(_: i32) {}
|
||||
| ^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
|
||||
= help: if you think this code should be accepted anyway, please report an issue with Miri
|
||||
= note: stack backtrace:
|
||||
0: g
|
||||
at tests/fail/tail_calls/signature-mismatch-arg.rs:LL:CC
|
||||
1: main
|
||||
at tests/fail/tail_calls/signature-mismatch-arg.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user