Auto merge of #142997 - workingjubilee:rollup-6lxec87, r=workingjubilee

Rollup of 15 pull requests

Successful merges:

 - rust-lang/rust#135731 (Implement parsing of pinned borrows)
 - rust-lang/rust#138780 (Add `#[loop_match]` for improved DFA codegen)
 - rust-lang/rust#142453 (Windows: make `read_dir` stop iterating after the first error is encountered)
 - rust-lang/rust#142633 (Error on invalid signatures for interrupt ABIs)
 - rust-lang/rust#142768 (Avoid a bitcast FFI call in transmuting)
 - rust-lang/rust#142825 (Port `#[track_caller]` to the new attribute system)
 - rust-lang/rust#142844 (Enable short-ice for Windows)
 - rust-lang/rust#142934 (Tweak `-Zmacro-stats` measurement.)
 - rust-lang/rust#142955 (Couple of test suite fixes for cg_clif)
 - rust-lang/rust#142977 (rustdoc: Don't mark `#[target_feature]` functions as ⚠)
 - rust-lang/rust#142980 (Reduce mismatched-lifetime-syntaxes suggestions to MaybeIncorrect)
 - rust-lang/rust#142982 (Corrected spelling mistake in c_str.rs)
 - rust-lang/rust#142983 (Taint body on invalid call ABI)
 - rust-lang/rust#142988 (Update wasm-component-ld to 0.5.14)
 - rust-lang/rust#142993 (Update cargo)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-06-25 04:05:47 +00:00
commit a17780db7b
163 changed files with 4230 additions and 430 deletions

View file

@ -3282,6 +3282,7 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_data_structures",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
@ -3316,6 +3317,7 @@ dependencies = [
"rustc_parse",
"rustc_session",
"rustc_span",
"rustc_target",
"thin-vec",
]
@ -4116,6 +4118,7 @@ dependencies = [
"rustc_apfloat",
"rustc_arena",
"rustc_ast",
"rustc_attr_data_structures",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
@ -5796,9 +5799,9 @@ dependencies = [
[[package]]
name = "wasi-preview1-component-adapter-provider"
version = "31.0.0"
version = "34.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b"
checksum = "aafa1e6af9a954a4bcf6ef420c33355d0ce84ddc6afbcba7bb6f05126f9120ae"
[[package]]
name = "wasm-bindgen"
@ -5860,9 +5863,9 @@ dependencies = [
[[package]]
name = "wasm-component-ld"
version = "0.5.13"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20"
checksum = "b015ec93764aa5517bc8b839efa9941b90be8ce680b1134f8224644ba1e48e3f"
dependencies = [
"anyhow",
"clap",
@ -5870,7 +5873,7 @@ dependencies = [
"libc",
"tempfile",
"wasi-preview1-component-adapter-provider",
"wasmparser 0.229.0",
"wasmparser 0.234.0",
"wat",
"windows-sys 0.59.0",
"winsplit",
@ -5897,12 +5900,12 @@ dependencies = [
[[package]]
name = "wasm-encoder"
version = "0.229.0"
version = "0.234.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2"
checksum = "170a0157eef517a179f2d20ed7c68df9c3f7f6c1c047782d488bf5a464174684"
dependencies = [
"leb128fmt",
"wasmparser 0.229.0",
"wasmparser 0.234.0",
]
[[package]]
@ -5917,14 +5920,14 @@ dependencies = [
[[package]]
name = "wasm-metadata"
version = "0.229.0"
version = "0.234.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d"
checksum = "a42fe3f5cbfb56fc65311ef827930d06189160038e81db62188f66b4bf468e3a"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder 0.229.0",
"wasmparser 0.229.0",
"wasm-encoder 0.234.0",
"wasmparser 0.234.0",
]
[[package]]
@ -5939,9 +5942,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.229.0"
version = "0.234.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c"
checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5"
dependencies = [
"bitflags",
"hashbrown",
@ -5950,15 +5953,6 @@ dependencies = [
"serde",
]
[[package]]
name = "wasmparser"
version = "0.234.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5"
dependencies = [
"bitflags",
]
[[package]]
name = "wasmparser"
version = "0.235.0"
@ -6411,9 +6405,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.229.0"
version = "0.234.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b"
checksum = "5a8888169acf4c6c4db535beb405b570eedac13215d6821ca9bd03190f7f8b8c"
dependencies = [
"anyhow",
"bitflags",
@ -6422,17 +6416,17 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"wasm-encoder 0.229.0",
"wasm-encoder 0.234.0",
"wasm-metadata",
"wasmparser 0.229.0",
"wasmparser 0.234.0",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.229.0"
version = "0.234.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6"
checksum = "465492df47d8dcc015a3b7f241aed8ea03688fee7c5e04162285c5b1a3539c8b"
dependencies = [
"anyhow",
"id-arena",
@ -6443,7 +6437,7 @@ dependencies = [
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser 0.229.0",
"wasmparser 0.234.0",
]
[[package]]

View file

@ -36,6 +36,10 @@ pub enum ExternAbi {
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
RustCold,
/// An always-invalid ABI that's used to test "this ABI is not supported by this platform"
/// in a platform-agnostic way.
RustInvalid,
/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
@ -157,6 +161,7 @@ abi_impls! {
RiscvInterruptS =><= "riscv-interrupt-s",
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustInvalid =><= "rust-invalid",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",

View file

@ -904,6 +904,10 @@ pub enum BorrowKind {
/// The resulting type is either `*const T` or `*mut T`
/// where `T = typeof($expr)`.
Raw,
/// A pinned borrow, `&pin const $expr` or `&pin mut $expr`.
/// The resulting type is either `Pin<&'a T>` or `Pin<&'a mut T>`
/// where `T = typeof($expr)` and `'a` is some lifetime.
Pin,
}
#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]

View file

@ -11,6 +11,7 @@ doctest = false
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }

View file

@ -4,6 +4,7 @@ use std::sync::Arc;
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::HirId;
@ -831,7 +832,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
) {
if self.tcx.features().async_fn_track_caller()
&& let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
&& attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
&& find_attr!(*attrs, AttributeKind::TrackCaller(_))
{
let unstable_span = self.mark_span_with_reason(
DesugaringKind::Async,

View file

@ -96,6 +96,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
ExternAbi::RustCold => {
Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
}
ExternAbi::RustInvalid => {
Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
}
ExternAbi::GpuKernel => Err(UnstableAbi {
abi,
feature: sym::abi_gpu_kernel,

View file

@ -18,5 +18,6 @@ rustc_macros = { path = "../rustc_macros" }
rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
thin-vec = "0.2.12"
# tidy-alphabetical-end

View file

@ -1,20 +1,25 @@
ast_passes_abi_custom_coroutine =
functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}`
ast_passes_abi_cannot_be_coroutine =
functions with the {$abi} ABI cannot be `{$coroutine_kind_str}`
.suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton
ast_passes_abi_custom_invalid_signature =
invalid signature for `extern "custom"` function
.note = functions with the `"custom"` ABI cannot have any parameters or return type
.suggestion = remove the parameters and return type
ast_passes_abi_custom_safe_foreign_function =
foreign functions with the `"custom"` ABI cannot be safe
foreign functions with the "custom" ABI cannot be safe
.suggestion = remove the `safe` keyword from this definition
ast_passes_abi_custom_safe_function =
functions with the `"custom"` ABI must be unsafe
functions with the "custom" ABI must be unsafe
.suggestion = add the `unsafe` keyword to this definition
ast_passes_abi_must_not_have_parameters_or_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the {$abi} ABI cannot have any parameters or return type
.suggestion = remove the parameters and return type
ast_passes_abi_must_not_have_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the "custom" ABI cannot have a return type
.help = remove the return type
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant

View file

@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use itertools::{Either, Itertools};
use rustc_abi::ExternAbi;
use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
use rustc_ast::ptr::P;
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
use rustc_ast::*;
@ -37,6 +37,7 @@ use rustc_session::lint::builtin::{
};
use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
use rustc_span::{Ident, Span, kw, sym};
use rustc_target::spec::{AbiMap, AbiMapping};
use thin_vec::thin_vec;
use crate::errors::{self, TildeConstReason};
@ -365,31 +366,77 @@ impl<'a> AstValidator<'a> {
}
}
/// An `extern "custom"` function must be unsafe, and must not have any parameters or return
/// type.
fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
/// Check that the signature of this function does not violate the constraints of its ABI.
fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) {
AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => {
match canon_abi {
CanonAbi::C
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::Arm(_)
| CanonAbi::GpuKernel
| CanonAbi::X86(_) => { /* nothing to check */ }
CanonAbi::Custom => {
// An `extern "custom"` function must be unsafe.
self.reject_safe_fn(abi, ctxt, sig);
// An `extern "custom"` function cannot be `async` and/or `gen`.
self.reject_coroutine(abi, sig);
// An `extern "custom"` function must have type `fn()`.
self.reject_params_or_return(abi, ident, sig);
}
CanonAbi::Interrupt(interrupt_kind) => {
// An interrupt handler cannot be `async` and/or `gen`.
self.reject_coroutine(abi, sig);
if let InterruptKind::X86 = interrupt_kind {
// "x86-interrupt" is special because it does have arguments.
// FIXME(workingjubilee): properly lint on acceptable input types.
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
self.dcx().emit_err(errors::AbiMustNotHaveReturnType {
span: ret_ty.span,
abi,
});
}
} else {
// An `extern "interrupt"` function must have type `fn()`.
self.reject_params_or_return(abi, ident, sig);
}
}
}
}
AbiMapping::Invalid => { /* ignore */ }
}
}
fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) {
let dcx = self.dcx();
// An `extern "custom"` function must be unsafe.
match sig.header.safety {
Safety::Unsafe(_) => { /* all good */ }
Safety::Safe(safe_span) => {
let safe_span =
self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
let source_map = self.sess.psess.source_map();
let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span));
dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
}
Safety::Default => match ctxt {
FnCtxt::Foreign => { /* all good */ }
FnCtxt::Free | FnCtxt::Assoc(_) => {
self.dcx().emit_err(errors::AbiCustomSafeFunction {
dcx.emit_err(errors::AbiCustomSafeFunction {
span: sig.span,
abi,
unsafe_span: sig.span.shrink_to_lo(),
});
}
},
}
}
// An `extern "custom"` function cannot be `async` and/or `gen`.
fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) {
if let Some(coroutine_kind) = sig.header.coroutine_kind {
let coroutine_kind_span = self
.sess
@ -397,14 +444,16 @@ impl<'a> AstValidator<'a> {
.source_map()
.span_until_non_whitespace(coroutine_kind.span().to(sig.span));
self.dcx().emit_err(errors::AbiCustomCoroutine {
self.dcx().emit_err(errors::AbiCannotBeCoroutine {
span: sig.span,
abi,
coroutine_kind_span,
coroutine_kind_str: coroutine_kind.as_str(),
});
}
}
// An `extern "custom"` function must not have any parameters or return type.
fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) {
let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
spans.push(ret_ty.span);
@ -415,11 +464,12 @@ impl<'a> AstValidator<'a> {
let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
let padding = if header_span.is_empty() { "" } else { " " };
self.dcx().emit_err(errors::AbiCustomInvalidSignature {
self.dcx().emit_err(errors::AbiMustNotHaveParametersOrReturnType {
spans,
symbol: ident.name,
suggestion_span,
padding,
abi,
});
}
}
@ -1199,9 +1249,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_foreign_fn_bodyless(*ident, body.as_deref());
self.check_foreign_fn_headerless(sig.header);
self.check_foreign_item_ascii_only(*ident);
if self.extern_mod_abi == Some(ExternAbi::Custom) {
self.check_custom_abi(FnCtxt::Foreign, ident, sig);
}
self.check_extern_fn_signature(
self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK),
FnCtxt::Foreign,
ident,
sig,
);
}
ForeignItemKind::TyAlias(box TyAlias {
defaultness,
@ -1411,9 +1464,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let FnKind::Fn(ctxt, _, fun) = fk
&& let Extern::Explicit(str_lit, _) = fun.sig.header.ext
&& let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
&& let Ok(abi) = ExternAbi::from_str(str_lit.symbol.as_str())
{
self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig);
}
self.check_c_variadic_type(fk);

View file

@ -1,5 +1,6 @@
//! Errors emitted by ast_passes.
use rustc_abi::ExternAbi;
use rustc_ast::ParamKindOrd;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
@ -845,6 +846,7 @@ pub(crate) struct AbiCustomSafeForeignFunction {
pub(crate) struct AbiCustomSafeFunction {
#[primary_span]
pub span: Span,
pub abi: ExternAbi,
#[suggestion(
ast_passes_suggestion,
@ -856,10 +858,11 @@ pub(crate) struct AbiCustomSafeFunction {
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_coroutine)]
pub(crate) struct AbiCustomCoroutine {
#[diag(ast_passes_abi_cannot_be_coroutine)]
pub(crate) struct AbiCannotBeCoroutine {
#[primary_span]
pub span: Span,
pub abi: ExternAbi,
#[suggestion(
ast_passes_suggestion,
@ -872,11 +875,12 @@ pub(crate) struct AbiCustomCoroutine {
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_invalid_signature)]
#[diag(ast_passes_abi_must_not_have_parameters_or_return_type)]
#[note]
pub(crate) struct AbiCustomInvalidSignature {
pub(crate) struct AbiMustNotHaveParametersOrReturnType {
#[primary_span]
pub spans: Vec<Span>,
pub abi: ExternAbi,
#[suggestion(
ast_passes_suggestion,
@ -888,3 +892,13 @@ pub(crate) struct AbiCustomInvalidSignature {
pub symbol: Symbol,
pub padding: &'static str,
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_must_not_have_return_type)]
#[note]
pub(crate) struct AbiMustNotHaveReturnType {
#[primary_span]
#[help]
pub span: Span,
pub abi: ExternAbi,
}

View file

@ -357,6 +357,10 @@ impl<'a> State<'a> {
self.word_nbsp("raw");
self.print_mutability(mutability, true);
}
ast::BorrowKind::Pin => {
self.word_nbsp("pin");
self.print_mutability(mutability, true);
}
}
self.print_expr_cond_paren(
expr,

View file

@ -212,6 +212,9 @@ pub enum AttributeKind {
first_span: Span,
},
/// Represents `#[const_continue]`.
ConstContinue(Span),
/// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
ConstStability {
stability: PartialConstStability,
@ -231,6 +234,9 @@ pub enum AttributeKind {
/// Represents `#[inline]` and `#[rustc_force_inline]`.
Inline(InlineAttr, Span),
/// Represents `#[loop_match]`.
LoopMatch(Span),
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),
@ -268,5 +274,8 @@ pub enum AttributeKind {
/// Span of the attribute.
span: Span,
},
/// Represents `#[track_caller]`
TrackCaller(Span),
// tidy-alphabetical-end
}

View file

@ -1,7 +1,7 @@
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
use rustc_feature::{AttributeTemplate, template};
use rustc_session::parse::feature_err;
use rustc_span::{Span, sym};
use rustc_span::{Span, Symbol, sym};
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, FinalizeContext, Stage};
@ -11,7 +11,7 @@ use crate::session_diagnostics::NakedFunctionIncompatibleAttribute;
pub(crate) struct OptimizeParser;
impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
const PATH: &[rustc_span::Symbol] = &[sym::optimize];
const PATH: &[Symbol] = &[sym::optimize];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
@ -44,7 +44,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
pub(crate) struct ColdParser;
impl<S: Stage> SingleAttributeParser<S> for ColdParser {
const PATH: &[rustc_span::Symbol] = &[sym::cold];
const PATH: &[Symbol] = &[sym::cold];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(Word);
@ -166,6 +166,24 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
}
}
pub(crate) struct TrackCallerParser;
impl<S: Stage> SingleAttributeParser<S> for TrackCallerParser {
const PATH: &[Symbol] = &[sym::track_caller];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
return None;
}
Some(AttributeKind::TrackCaller(cx.attr_span))
}
}
pub(crate) struct NoMangleParser;
impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {

View file

@ -0,0 +1,31 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
pub(crate) struct LoopMatchParser;
impl<S: Stage> SingleAttributeParser<S> for LoopMatchParser {
const PATH: &[Symbol] = &[sym::loop_match];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::LoopMatch(cx.attr_span))
}
}
pub(crate) struct ConstContinueParser;
impl<S: Stage> SingleAttributeParser<S> for ConstContinueParser {
const PATH: &[Symbol] = &[sym::const_continue];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::ConstContinue(cx.attr_span))
}
}

View file

@ -32,6 +32,7 @@ pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod inline;
pub(crate) mod lint_helpers;
pub(crate) mod loop_match;
pub(crate) mod must_use;
pub(crate) mod repr;
pub(crate) mod semantics;

View file

@ -15,11 +15,14 @@ use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::codegen_attrs::{ColdParser, NakedParser, NoMangleParser, OptimizeParser};
use crate::attributes::codegen_attrs::{
ColdParser, NakedParser, NoMangleParser, OptimizeParser, TrackCallerParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::must_use::MustUseParser;
use crate::attributes::repr::{AlignParser, ReprParser};
use crate::attributes::semantics::MayDangleParser;
@ -111,9 +114,11 @@ attribute_parsers!(
// tidy-alphabetical-start
Single<AsPtrParser>,
Single<ColdParser>,
Single<ConstContinueParser>,
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<InlineParser>,
Single<LoopMatchParser>,
Single<MayDangleParser>,
Single<MustUseParser>,
Single<NoMangleParser>,
@ -121,6 +126,7 @@ attribute_parsers!(
Single<PubTransparentParser>,
Single<RustcForceInlineParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TrackCallerParser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
];

View file

@ -151,20 +151,6 @@ rm tests/ui/process/process-panic-after-fork.rs # same
cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist
cat <<EOF | git apply -
diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
index 30387af428c..f7895b12961 100644
--- a/tests/run-make/linker-warning/rmake.rs
+++ b/tests/run-make/linker-warning/rmake.rs
@@ -57,7 +57,8 @@ fn main() {
.actual_text("(linker error)", out.stderr())
- .normalize(r#"/rustc[^/]*/"#, "/rustc/")
+ .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/")
+ .normalize("libpanic_abort", "libpanic_unwind")
.normalize(
regex::escape(run_make_support::build_root().to_str().unwrap()),
"/build-root",
)
.normalize(r#""[^"]*\/symbols.o""#, "\\"/symbols.o\\"")
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 073116933bd..c3e4578204d 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs

View file

@ -95,17 +95,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
// In these cases, we bail from performing further checks that are only meaningful for
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
// report a delayed bug, just in case `check_attr` isn't doing its job.
let fn_sig = || {
let fn_sig = |attr_span| {
use DefKind::*;
let def_kind = tcx.def_kind(did);
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
Some(tcx.fn_sig(did))
} else {
tcx.dcx().span_delayed_bug(
attr.span(),
"this attribute can only be applied to functions",
);
tcx.dcx()
.span_delayed_bug(attr_span, "this attribute can only be applied to functions");
None
}
};
@ -142,6 +140,29 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
});
}
}
AttributeKind::TrackCaller(attr_span) => {
let is_closure = tcx.is_closure_like(did.to_def_id());
if !is_closure
&& let Some(fn_sig) = fn_sig(*attr_span)
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
{
tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
}
if is_closure
&& !tcx.features().closure_track_caller()
&& !attr_span.allows_unstable(sym::closure_track_caller)
{
feature_err(
&tcx.sess,
sym::closure_track_caller,
*attr_span,
"`#[track_caller]` on closures is currently unstable",
)
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
}
_ => {}
}
}
@ -202,29 +223,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
sym::track_caller => {
let is_closure = tcx.is_closure_like(did.to_def_id());
if !is_closure
&& let Some(fn_sig) = fn_sig()
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
{
tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() });
}
if is_closure
&& !tcx.features().closure_track_caller()
&& !attr.span().allows_unstable(sym::closure_track_caller)
{
feature_err(
&tcx.sess,
sym::closure_track_caller,
attr.span(),
"`#[track_caller]` on closures is currently unstable",
)
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
}
sym::export_name => {
if let Some(s) = attr.value_str() {
if s.as_str().contains('\0') {

View file

@ -1123,7 +1123,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// While optimizations will remove no-op transmutes, they might still be
// there in debug or things that aren't no-op in MIR because they change
// the Rust type but not the underlying layout/niche.
if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
if from_scalar == to_scalar {
return imm;
}
@ -1142,7 +1142,13 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
(Int(..) | Float(_), Int(..) | Float(_)) => {
if from_backend_ty == to_backend_ty {
imm
} else {
bx.bitcast(imm, to_backend_ty)
}
}
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
(Pointer(..), Int(..)) => {

View file

@ -600,11 +600,13 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
}),
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
}),
hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
ccx.dcx().create_err(errors::MutableRefEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
})
}
}
}
}

View file

@ -15,15 +15,11 @@ pub struct MacroStat {
/// Number of uses of the macro.
pub uses: usize,
/// Net increase in number of lines of code (when pretty-printed), i.e.
/// `lines(output) - lines(invocation)`. Can be negative because a macro
/// output may be smaller than the invocation.
pub lines: isize,
/// Number of lines of code (when pretty-printed).
pub lines: usize,
/// Net increase in number of lines of code (when pretty-printed), i.e.
/// `bytes(output) - bytes(invocation)`. Can be negative because a macro
/// output may be smaller than the invocation.
pub bytes: isize,
/// Number of bytes of code (when pretty-printed).
pub bytes: usize,
}
pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String {
@ -131,16 +127,12 @@ pub(crate) fn update_macro_stats(
input: &str,
fragment: &AstFragment,
) {
fn lines_and_bytes(s: &str) -> (usize, usize) {
(s.trim_end().split('\n').count(), s.len())
}
// Measure the size of the output by pretty-printing it and counting
// the lines and bytes.
let name = Symbol::intern(&pprust::path_to_string(path));
let output = fragment.to_string();
let (in_l, in_b) = lines_and_bytes(input);
let (out_l, out_b) = lines_and_bytes(&output);
let num_lines = output.trim_end().split('\n').count();
let num_bytes = output.len();
// This code is useful for debugging `-Zmacro-stats`. For every
// invocation it prints the full input and output.
@ -157,7 +149,7 @@ pub(crate) fn update_macro_stats(
{name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
-------------------------------\n\
{input}\n\
-- ({in_l} lines, {in_b} bytes) --> ({out_l} lines, {out_b} bytes) --\n\
-- {num_lines} lines, {num_bytes} bytes --\n\
{output}\n\
"
);
@ -166,6 +158,6 @@ pub(crate) fn update_macro_stats(
// The recorded size is the difference between the input and the output.
let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default());
entry.uses += 1;
entry.lines += out_l as isize - in_l as isize;
entry.bytes += out_b as isize - in_b as isize;
entry.lines += num_lines;
entry.bytes += num_bytes;
}

View file

@ -657,6 +657,19 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
),
// The `#[loop_match]` and `#[const_continue]` attributes are part of the
// lang experiment for RFC 3720 tracked in:
//
// - https://github.com/rust-lang/rust/issues/132306
gated!(
const_continue, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::No, loop_match, experimental!(const_continue)
),
gated!(
loop_match, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
),
// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================

View file

@ -557,6 +557,8 @@ declare_features! (
/// Allows using `#[link(kind = "link-arg", name = "...")]`
/// to pass custom arguments to the linker.
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
/// Allows fused `loop`/`match` for direct intraprocedural jumps.
(incomplete, loop_match, "CURRENT_RUSTC_VERSION", Some(132306)),
/// 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.

View file

@ -1,5 +1,5 @@
hir_analysis_abi_custom_clothed_function =
items with the `"custom"` ABI can only be declared externally or defined via naked functions
items with the "custom" ABI can only be declared externally or defined via naked functions
.suggestion = convert this to an `#[unsafe(naked)]` function
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`

View file

@ -1,14 +1,15 @@
use std::ops::Not;
use rustc_abi::ExternAbi;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_hir as hir;
use rustc_hir::Node;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt, TypingMode};
use rustc_session::config::EntryFnType;
use rustc_span::Span;
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_span::{Span, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
@ -98,8 +99,10 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
error = true;
}
for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span });
if let Some(attr_span) =
find_attr!(tcx.get_all_attrs(main_def_id), AttributeKind::TrackCaller(span) => *span)
{
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span });
error = true;
}

View file

@ -1335,6 +1335,10 @@ impl<'a> State<'a> {
self.word_nbsp("raw");
self.print_mutability(mutability, true);
}
hir::BorrowKind::Pin => {
self.word_nbsp("pin");
self.print_mutability(mutability, true);
}
}
self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix);
}

View file

@ -79,6 +79,9 @@ hir_typeck_cast_unknown_pointer = cannot cast {$to ->
.note = the type information given here is insufficient to check whether the pointer cast is valid
.label_from = the type information given here is insufficient to check whether the pointer cast is valid
hir_typeck_const_continue_bad_label =
`#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
hir_typeck_const_select_must_be_const = this argument must be a `const fn`
.help = consult the documentation on `const_eval_select` for more information

View file

@ -156,7 +156,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn check_call_abi(&self, abi: ExternAbi, span: Span) {
let canon_abi = match AbiMap::from_target(&self.sess().target).canonize_abi(abi, false) {
AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => canon_abi,
AbiMapping::Invalid => return,
AbiMapping::Invalid => {
// This should be reported elsewhere, but we want to taint this body
// so that we don't try to evaluate calls to ABIs that are invalid.
let guar = self.dcx().span_delayed_bug(
span,
format!("invalid abi for platform should have reported an error: {abi}"),
);
self.set_tainted_by_errors(guar);
return;
}
};
let valid = match canon_abi {

View file

@ -1167,3 +1167,10 @@ pub(crate) struct AbiCannotBeCalled {
pub span: Span,
pub abi: ExternAbi,
}
#[derive(Diagnostic)]
#[diag(hir_typeck_const_continue_bad_label)]
pub(crate) struct ConstContinueBadLabel {
#[primary_span]
pub span: Span,
}

View file

@ -690,7 +690,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_named_place_expr(oprnd);
Ty::new_ptr(self.tcx, ty, mutbl)
}
hir::BorrowKind::Ref => {
hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
// Note: at this point, we cannot say what the best lifetime
// is to use for resulting pointer. We want to use the
// shortest lifetime possible so as to avoid spurious borrowck
@ -706,7 +706,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// whose address was taken can actually be made to live as long
// as it needs to live.
let region = self.next_region_var(infer::BorrowRegion(expr.span));
Ty::new_ref(self.tcx, region, ty, mutbl)
match kind {
hir::BorrowKind::Ref => Ty::new_ref(self.tcx, region, ty, mutbl),
hir::BorrowKind::Pin => Ty::new_pinned_ref(self.tcx, region, ty, mutbl),
_ => unreachable!(),
}
}
}
}

View file

@ -2,6 +2,8 @@ use std::collections::BTreeMap;
use std::fmt;
use Context::*;
use rustc_ast::Label;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
@ -14,8 +16,9 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::{BytePos, Span};
use crate::errors::{
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ConstContinueBadLabel,
ContinueLabeledBlock, OutsideLoop, OutsideLoopSuggestion, UnlabeledCfInWhileCondition,
UnlabeledInLabeledBlock,
};
/// The context in which a block is encountered.
@ -37,6 +40,11 @@ enum Context {
AnonConst,
/// E.g. `const { ... }`.
ConstBlock,
/// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
LoopMatch {
/// The label of the labeled block (not of the loop itself).
labeled_block: Label,
},
}
#[derive(Clone)]
@ -141,7 +149,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
}
}
hir::ExprKind::Loop(ref b, _, source, _) => {
self.with_context(Loop(source), |v| v.visit_block(b));
let cx = match self.is_loop_match(e, b) {
Some(labeled_block) => LoopMatch { labeled_block },
None => Loop(source),
};
self.with_context(cx, |v| v.visit_block(b));
}
hir::ExprKind::Closure(&hir::Closure {
ref fn_decl, body, fn_decl_span, kind, ..
@ -197,6 +210,23 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
Err(hir::LoopIdError::UnresolvedLabel) => None,
};
// A `#[const_continue]` must break to a block in a `#[loop_match]`.
if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) {
if let Some(break_label) = break_label.label {
let is_target_label = |cx: &Context| match cx {
Context::LoopMatch { labeled_block } => {
break_label.ident.name == labeled_block.ident.name
}
_ => false,
};
if !self.cx_stack.iter().rev().any(is_target_label) {
let span = break_label.ident.span;
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
}
}
}
if let Some(Node::Block(_)) = loop_id.map(|id| self.tcx.hir_node(id)) {
return;
}
@ -299,7 +329,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
cx_pos: usize,
) {
match self.cx_stack[cx_pos] {
LabeledBlock | Loop(_) => {}
LabeledBlock | Loop(_) | LoopMatch { .. } => {}
Closure(closure_span) => {
self.tcx.dcx().emit_err(BreakInsideClosure {
span,
@ -380,4 +410,36 @@ impl<'hir> CheckLoopVisitor<'hir> {
});
}
}
/// Is this a loop annotated with `#[loop_match]` that looks syntactically sound?
fn is_loop_match(
&self,
e: &'hir hir::Expr<'hir>,
body: &'hir hir::Block<'hir>,
) -> Option<Label> {
if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) {
return None;
}
// NOTE: Diagnostics are emitted during MIR construction.
// Accept either `state = expr` or `state = expr;`.
let loop_body_expr = match body.stmts {
[] => match body.expr {
Some(expr) => expr,
None => return None,
},
[single] if body.expr.is_none() => match single.kind {
hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
_ => return None,
},
[..] => return None,
};
let hir::ExprKind::Assign(_, rhs_expr, _) = loop_body_expr.kind else { return None };
let hir::ExprKind::Block(_, label) = rhs_expr.kind else { return None };
label
}
}

View file

@ -355,9 +355,9 @@ fn print_macro_stats(ecx: &ExtCtxt<'_>) {
"{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}",
name,
thousands::usize_with_underscores(uses),
thousands::isize_with_underscores(lines),
thousands::usize_with_underscores(lines),
thousands::f64p1_with_underscores(avg_lines),
thousands::isize_with_underscores(bytes),
thousands::usize_with_underscores(bytes),
thousands::f64p1_with_underscores(avg_bytes),
);
}

View file

@ -1184,11 +1184,11 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
if fn_kind.asyncness().is_async()
&& !cx.tcx.features().async_fn_track_caller()
// Now, check if the function has the `#[track_caller]` attribute
&& let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
&& let Some(attr_span) = find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::TrackCaller(span) => *span)
{
cx.emit_span_lint(
UNGATED_ASYNC_FN_TRACK_CALLER,
attr.span(),
attr_span,
BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },
);
}

View file

@ -3278,7 +3278,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
diag.multipart_suggestion_with_style(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit,
suggestions,
Applicability::MachineApplicable,
Applicability::MaybeIncorrect,
style(tool_only),
);
}
@ -3293,7 +3293,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
diag.multipart_suggestion_with_style(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed,
suggestions,
Applicability::MachineApplicable,
Applicability::MaybeIncorrect,
style(tool_only),
);
}
@ -3308,7 +3308,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
diag.multipart_suggestion_with_style(
msg,
suggestions,
Applicability::MachineApplicable,
Applicability::MaybeIncorrect,
style(tool_only),
);
}

View file

@ -378,6 +378,14 @@ pub enum ExprKind<'tcx> {
Loop {
body: ExprId,
},
/// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression.
LoopMatch {
/// The state variable that is updated, and also the scrutinee of the match.
state: ExprId,
region_scope: region::Scope,
arms: Box<[ArmId]>,
match_span: Span,
},
/// Special expression representing the `let` part of an `if let` or similar construct
/// (including `if let` guards in match arms, and let-chains formed by `&&`).
///
@ -454,6 +462,11 @@ pub enum ExprKind<'tcx> {
Continue {
label: region::Scope,
},
/// A `#[const_continue] break` expression.
ConstContinue {
label: region::Scope,
value: ExprId,
},
/// A `return` expression.
Return {
value: Option<ExprId>,

View file

@ -83,7 +83,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
visitor.visit_pat(pat);
}
Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
Match { scrutinee, ref arms, .. } => {
LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => {
visitor.visit_expr(&visitor.thir()[scrutinee]);
for &arm in &**arms {
visitor.visit_arm(&visitor.thir()[arm]);
@ -108,6 +108,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
}
}
Continue { label: _ } => {}
ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]),
Return { value } => {
if let Some(value) = value {
visitor.visit_expr(&visitor.thir()[value])

View file

@ -1253,7 +1253,8 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
| CCmseNonSecureCall
| CCmseNonSecureEntry
| Custom
| Unadjusted => false,
| Unadjusted
| RustInvalid => false,
Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
}
}

View file

@ -11,6 +11,7 @@ rustc_abi = { path = "../rustc_abi" }
rustc_apfloat = "0.2.0"
rustc_arena = { path = "../rustc_arena" }
rustc_ast = { path = "../rustc_ast" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }

View file

@ -84,6 +84,15 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
.label = this value is too generic
.note = the value must be a literal or a monomorphic const
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
.label = this value must be a literal or a monomorphic const
mir_build_const_defined_here = constant defined here
mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
@ -212,6 +221,30 @@ mir_build_literal_in_range_out_of_bounds =
literal out of range for `{$ty}`
.label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`
mir_build_loop_match_arm_with_guard =
match arms that are part of a `#[loop_match]` cannot have guards
mir_build_loop_match_bad_rhs =
this expression must be a single `match` wrapped in a labeled block
mir_build_loop_match_bad_statements =
statements are not allowed in this position within a `#[loop_match]`
mir_build_loop_match_invalid_match =
invalid match on `#[loop_match]` state
.note = a local variable must be the scrutinee within a `#[loop_match]`
mir_build_loop_match_invalid_update =
invalid update of the `#[loop_match]` state
.label = the assignment must update this variable
mir_build_loop_match_missing_assignment =
expected a single assignment expression
mir_build_loop_match_unsupported_type =
this `#[loop_match]` state value has type `{$ty}`, which is not supported
.note = only integers, floats, bool, char, and enums without fields are supported
mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
lower range bound must be less than or equal to upper
.label = lower bound larger than upper bound

View file

@ -565,12 +565,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Match { .. }
| ExprKind::If { .. }
| ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::Block { .. }
| ExprKind::Let { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. }
| ExprKind::Literal { .. }

View file

@ -538,6 +538,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::RawBorrow { .. }
| ExprKind::Adt { .. }
| ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::LogicalOp { .. }
| ExprKind::Call { .. }
| ExprKind::Field { .. }
@ -548,6 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::UpvarRef { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. }
| ExprKind::InlineAsm { .. }

View file

@ -83,9 +83,11 @@ impl Category {
| ExprKind::NamedConst { .. } => Some(Category::Constant),
ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::Block { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. } =>
// FIXME(#27840) these probably want their own

View file

@ -8,15 +8,16 @@ use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::thir::*;
use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc_span::DUMMY_SP;
use rustc_span::source_map::Spanned;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::{debug, instrument};
use crate::builder::expr::category::{Category, RvalueFunc};
use crate::builder::matches::DeclareLetBindings;
use crate::builder::matches::{DeclareLetBindings, HasMatchGuard};
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which
@ -244,6 +245,122 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None
})
}
ExprKind::LoopMatch { state, region_scope, match_span, ref arms } => {
// Intuitively, this is a combination of a loop containing a labeled block
// containing a match.
//
// The only new bit here is that the lowering of the match is wrapped in a
// `in_const_continuable_scope`, which makes the match arms and their target basic
// block available to the lowering of `#[const_continue]`.
fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
ty::Adt(adt_def, _) => match adt_def.adt_kind() {
ty::AdtKind::Struct | ty::AdtKind::Union => false,
ty::AdtKind::Enum => {
adt_def.variants().iter().all(|v| v.fields.is_empty())
}
},
_ => false,
}
}
let state_ty = this.thir.exprs[state].ty;
if !is_supported_loop_match_type(state_ty) {
let span = this.thir.exprs[state].span;
this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
}
let loop_block = this.cfg.start_new_block();
// Start the loop.
this.cfg.goto(block, source_info, loop_block);
this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
// Logic for `loop`.
let mut body_block = this.cfg.start_new_block();
this.cfg.terminate(
loop_block,
source_info,
TerminatorKind::FalseUnwind {
real_target: body_block,
unwind: UnwindAction::Continue,
},
);
this.diverge_from(loop_block);
// Logic for `match`.
let scrutinee_place_builder =
unpack!(body_block = this.as_place_builder(body_block, state));
let scrutinee_span = this.thir.exprs[state].span;
let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
let mut patterns = Vec::with_capacity(arms.len());
for &arm_id in arms.iter() {
let arm = &this.thir[arm_id];
if let Some(guard) = arm.guard {
let span = this.thir.exprs[guard].span;
this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
}
patterns.push((&*arm.pattern, HasMatchGuard::No));
}
// The `built_tree` maps match arms to their basic block (where control flow
// jumps to when a value matches the arm). This structure is stored so that a
// `#[const_continue]` can figure out what basic block to jump to.
let built_tree = this.lower_match_tree(
body_block,
scrutinee_span,
&scrutinee_place_builder,
match_start_span,
patterns,
false,
);
let state_place = scrutinee_place_builder.to_place(this);
// This is logic for the labeled block: a block is a drop scope, hence
// `in_scope`, and a labeled block can be broken out of with a `break 'label`,
// hence the `in_breakable_scope`.
//
// Then `in_const_continuable_scope` stores information for the lowering of
// `#[const_continue]`, and finally the match is lowered in the standard way.
unpack!(
body_block = this.in_scope(
(region_scope, source_info),
LintLevel::Inherited,
move |this| {
this.in_breakable_scope(None, state_place, expr_span, |this| {
Some(this.in_const_continuable_scope(
arms.clone(),
built_tree.clone(),
state_place,
expr_span,
|this| {
this.lower_match_arms(
destination,
scrutinee_place_builder,
scrutinee_span,
arms,
built_tree,
this.source_info(match_span),
)
},
))
})
}
)
);
this.cfg.goto(body_block, source_info, loop_block);
// Loops are only exited by `break` expressions.
None
})
}
ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Box<[_]> = args
@ -601,6 +718,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Break { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. } => {

View file

@ -98,6 +98,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Break { label, value } => {
this.break_scope(block, value, BreakableTarget::Break(label), source_info)
}
ExprKind::ConstContinue { label, value } => {
this.break_const_continuable_scope(block, value, label, source_info)
}
ExprKind::Return { value } => {
this.break_scope(block, value, BreakableTarget::Return, source_info)
}

View file

@ -18,7 +18,9 @@ use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::mir::{self, *};
use rustc_middle::thir::{self, *};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
use rustc_pattern_analysis::constructor::RangeEnd;
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
use tracing::{debug, instrument};
@ -426,7 +428,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// (by [Builder::lower_match_tree]).
///
/// `outer_source_info` is the SourceInfo for the whole match.
fn lower_match_arms(
pub(crate) fn lower_match_arms(
&mut self,
destination: Place<'tcx>,
scrutinee_place_builder: PlaceBuilder<'tcx>,
@ -1395,7 +1397,7 @@ pub(crate) struct ArmHasGuard(pub(crate) bool);
/// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will
/// branch to `success_block` when the matched value matches the corresponding pattern. If there is
/// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns.
#[derive(Debug)]
#[derive(Debug, Clone)]
struct MatchTreeSubBranch<'tcx> {
span: Span,
/// The block that is branched to if the corresponding subpattern matches.
@ -1411,7 +1413,7 @@ struct MatchTreeSubBranch<'tcx> {
}
/// A branch in the output of match lowering.
#[derive(Debug)]
#[derive(Debug, Clone)]
struct MatchTreeBranch<'tcx> {
sub_branches: Vec<MatchTreeSubBranch<'tcx>>,
}
@ -1430,8 +1432,8 @@ struct MatchTreeBranch<'tcx> {
/// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each
/// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a
/// different place.
#[derive(Debug)]
struct BuiltMatchTree<'tcx> {
#[derive(Debug, Clone)]
pub(crate) struct BuiltMatchTree<'tcx> {
branches: Vec<MatchTreeBranch<'tcx>>,
otherwise_block: BasicBlock,
/// If any of the branches had a guard, we collect here the places and locals to fakely borrow
@ -1489,7 +1491,7 @@ impl<'tcx> MatchTreeBranch<'tcx> {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HasMatchGuard {
pub(crate) enum HasMatchGuard {
Yes,
No,
}
@ -1504,7 +1506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
/// or not (for `let` and `match`). In the refutable case we return the block to which we branch
/// on failure.
fn lower_match_tree(
pub(crate) fn lower_match_tree(
&mut self,
block: BasicBlock,
scrutinee_span: Span,
@ -1890,7 +1892,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);
candidate.or_span = Some(match_pair.pattern_span);
candidate.subcandidates = pats
.into_vec()
.into_iter()
.map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
.collect();
@ -2864,4 +2865,129 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
true
}
/// Attempt to statically pick the `BasicBlock` that a value would resolve to at runtime.
pub(crate) fn static_pattern_match(
&self,
cx: &RustcPatCtxt<'_, 'tcx>,
valtree: ValTree<'tcx>,
arms: &[ArmId],
built_match_tree: &BuiltMatchTree<'tcx>,
) -> Option<BasicBlock> {
let it = arms.iter().zip(built_match_tree.branches.iter());
for (&arm_id, branch) in it {
let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern);
// Peel off or-patterns if they exist.
if let rustc_pattern_analysis::rustc::Constructor::Or = pat.ctor() {
for pat in pat.iter_fields() {
// For top-level or-patterns (the only ones we accept right now), when the
// bindings are the same (e.g. there are none), the sub_branch is stored just
// once.
let sub_branch = branch
.sub_branches
.get(pat.idx)
.or_else(|| branch.sub_branches.last())
.unwrap();
match self.static_pattern_match_inner(valtree, &pat.pat) {
true => return Some(sub_branch.success_block),
false => continue,
}
}
} else if self.static_pattern_match_inner(valtree, &pat) {
return Some(branch.sub_branches[0].success_block);
}
}
None
}
/// Helper for [`Self::static_pattern_match`], checking whether the value represented by the
/// `ValTree` matches the given pattern. This function does not recurse, meaning that it does
/// not handle or-patterns, or patterns for types with fields.
fn static_pattern_match_inner(
&self,
valtree: ty::ValTree<'tcx>,
pat: &DeconstructedPat<'_, 'tcx>,
) -> bool {
use rustc_pattern_analysis::constructor::{IntRange, MaybeInfiniteInt};
use rustc_pattern_analysis::rustc::Constructor;
match pat.ctor() {
Constructor::Variant(variant_index) => {
let ValTreeKind::Branch(box [actual_variant_idx]) = *valtree else {
bug!("malformed valtree for an enum")
};
let ValTreeKind::Leaf(actual_variant_idx) = ***actual_variant_idx else {
bug!("malformed valtree for an enum")
};
*variant_index == VariantIdx::from_u32(actual_variant_idx.to_u32())
}
Constructor::IntRange(int_range) => {
let size = pat.ty().primitive_size(self.tcx);
let actual_int = valtree.unwrap_leaf().to_bits(size);
let actual_int = if pat.ty().is_signed() {
MaybeInfiniteInt::new_finite_int(actual_int, size.bits())
} else {
MaybeInfiniteInt::new_finite_uint(actual_int)
};
IntRange::from_singleton(actual_int).is_subrange(int_range)
}
Constructor::Bool(pattern_value) => match valtree.unwrap_leaf().try_to_bool() {
Ok(actual_value) => *pattern_value == actual_value,
Err(()) => bug!("bool value with invalid bits"),
},
Constructor::F16Range(l, h, end) => {
let actual = valtree.unwrap_leaf().to_f16();
match end {
RangeEnd::Included => (*l..=*h).contains(&actual),
RangeEnd::Excluded => (*l..*h).contains(&actual),
}
}
Constructor::F32Range(l, h, end) => {
let actual = valtree.unwrap_leaf().to_f32();
match end {
RangeEnd::Included => (*l..=*h).contains(&actual),
RangeEnd::Excluded => (*l..*h).contains(&actual),
}
}
Constructor::F64Range(l, h, end) => {
let actual = valtree.unwrap_leaf().to_f64();
match end {
RangeEnd::Included => (*l..=*h).contains(&actual),
RangeEnd::Excluded => (*l..*h).contains(&actual),
}
}
Constructor::F128Range(l, h, end) => {
let actual = valtree.unwrap_leaf().to_f128();
match end {
RangeEnd::Included => (*l..=*h).contains(&actual),
RangeEnd::Excluded => (*l..*h).contains(&actual),
}
}
Constructor::Wildcard => true,
// These we may eventually support:
Constructor::Struct
| Constructor::Ref
| Constructor::DerefPattern(_)
| Constructor::Slice(_)
| Constructor::UnionField
| Constructor::Or
| Constructor::Str(_) => bug!("unsupported pattern constructor {:?}", pat.ctor()),
// These should never occur here:
Constructor::Opaque(_)
| Constructor::Never
| Constructor::NonExhaustive
| Constructor::Hidden
| Constructor::Missing
| Constructor::PrivateUninhabited => {
bug!("unsupported pattern constructor {:?}", pat.ctor())
}
}
}
}

View file

@ -83,20 +83,24 @@ that contains only loops and breakable blocks. It tracks where a `break`,
use std::mem;
use interpret::ErrorHandled;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::HirId;
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::{ExprId, LintLevel};
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::mir::{self, *};
use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind, LintLevel};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
use rustc_middle::{bug, span_bug};
use rustc_pattern_analysis::rustc::RustcPatCtxt;
use rustc_session::lint::Level;
use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument};
use super::matches::BuiltMatchTree;
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
#[derive(Debug)]
pub(crate) struct Scopes<'tcx> {
@ -105,6 +109,8 @@ pub(crate) struct Scopes<'tcx> {
/// The current set of breakable scopes. See module comment for more details.
breakable_scopes: Vec<BreakableScope<'tcx>>,
const_continuable_scopes: Vec<ConstContinuableScope<'tcx>>,
/// The scope of the innermost if-then currently being lowered.
if_then_scope: Option<IfThenScope>,
@ -174,6 +180,20 @@ struct BreakableScope<'tcx> {
continue_drops: Option<DropTree>,
}
#[derive(Debug)]
struct ConstContinuableScope<'tcx> {
/// The scope for the `#[loop_match]` which its `#[const_continue]`s will jump to.
region_scope: region::Scope,
/// The place of the state of a `#[loop_match]`, which a `#[const_continue]` must update.
state_place: Place<'tcx>,
arms: Box<[ArmId]>,
built_match_tree: BuiltMatchTree<'tcx>,
/// Drops that happen on a `#[const_continue]`
const_continue_drops: DropTree,
}
#[derive(Debug)]
struct IfThenScope {
/// The if-then scope or arm scope
@ -461,6 +481,7 @@ impl<'tcx> Scopes<'tcx> {
Self {
scopes: Vec::new(),
breakable_scopes: Vec::new(),
const_continuable_scopes: Vec::new(),
if_then_scope: None,
unwind_drops: DropTree::new(),
coroutine_drops: DropTree::new(),
@ -552,6 +573,59 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
/// Start a const-continuable scope, which tracks where `#[const_continue] break` should
/// branch to.
pub(crate) fn in_const_continuable_scope<F>(
&mut self,
arms: Box<[ArmId]>,
built_match_tree: BuiltMatchTree<'tcx>,
state_place: Place<'tcx>,
span: Span,
f: F,
) -> BlockAnd<()>
where
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<()>,
{
let region_scope = self.scopes.topmost();
let scope = ConstContinuableScope {
region_scope,
state_place,
const_continue_drops: DropTree::new(),
arms,
built_match_tree,
};
self.scopes.const_continuable_scopes.push(scope);
let normal_exit_block = f(self);
let const_continue_scope = self.scopes.const_continuable_scopes.pop().unwrap();
assert!(const_continue_scope.region_scope == region_scope);
let break_block = self.build_exit_tree(
const_continue_scope.const_continue_drops,
region_scope,
span,
None,
);
match (normal_exit_block, break_block) {
(block, None) => block,
(normal_block, Some(exit_block)) => {
let target = self.cfg.start_new_block();
let source_info = self.source_info(span);
self.cfg.terminate(
normal_block.into_block(),
source_info,
TerminatorKind::Goto { target },
);
self.cfg.terminate(
exit_block.into_block(),
source_info,
TerminatorKind::Goto { target },
);
target.unit()
}
}
}
/// Start an if-then scope which tracks drop for `if` expressions and `if`
/// guards.
///
@ -742,6 +816,190 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.start_new_block().unit()
}
/// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`.
fn eval_unevaluated_mir_constant_to_valtree(
&self,
constant: ConstOperand<'tcx>,
) -> Result<(ty::ValTree<'tcx>, Ty<'tcx>), interpret::ErrorHandled> {
assert!(!constant.const_.ty().has_param());
let (uv, ty) = match constant.const_ {
mir::Const::Unevaluated(uv, ty) => (uv.shrink(), ty),
mir::Const::Ty(_, c) => match c.kind() {
// A constant that came from a const generic but was then used as an argument to
// old-style simd_shuffle (passing as argument instead of as a generic param).
ty::ConstKind::Value(cv) => return Ok((cv.valtree, cv.ty)),
other => span_bug!(constant.span, "{other:#?}"),
},
mir::Const::Val(mir::ConstValue::Scalar(mir::interpret::Scalar::Int(val)), ty) => {
return Ok((ValTree::from_scalar_int(self.tcx, val), ty));
}
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
// a constant and write that value back into `Operand`s. This could happen, but is
// unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take
// a lot of care around intrinsics. For an issue to happen here, it would require a
// macro expanding to a `simd_shuffle` call without wrapping the constant argument in a
// `const {}` block, but the user pass through arbitrary expressions.
// FIXME(oli-obk): Replace the magic const generic argument of `simd_shuffle` with a
// real const generic, and get rid of this entire function.
other => span_bug!(constant.span, "{other:#?}"),
};
match self.tcx.const_eval_resolve_for_typeck(self.typing_env(), uv, constant.span) {
Ok(Ok(valtree)) => Ok((valtree, ty)),
Ok(Err(ty)) => span_bug!(constant.span, "could not convert {ty:?} to a valtree"),
Err(e) => Err(e),
}
}
/// Sets up the drops for jumping from `block` to `scope`.
pub(crate) fn break_const_continuable_scope(
&mut self,
mut block: BasicBlock,
value: ExprId,
scope: region::Scope,
source_info: SourceInfo,
) -> BlockAnd<()> {
let span = source_info.span;
// A break can only break out of a scope, so the value should be a scope.
let rustc_middle::thir::ExprKind::Scope { value, .. } = self.thir[value].kind else {
span_bug!(span, "break value must be a scope")
};
let constant = match &self.thir[value].kind {
ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => {
assert!(matches!(base, AdtExprBase::None));
assert!(fields.is_empty());
ConstOperand {
span: self.thir[value].span,
user_ty: None,
const_: Const::Ty(
self.thir[value].ty,
ty::Const::new_value(
self.tcx,
ValTree::from_branches(
self.tcx,
[ValTree::from_scalar_int(self.tcx, variant_index.as_u32().into())],
),
self.thir[value].ty,
),
),
}
}
_ => self.as_constant(&self.thir[value]),
};
let break_index = self
.scopes
.const_continuable_scopes
.iter()
.rposition(|const_continuable_scope| const_continuable_scope.region_scope == scope)
.unwrap_or_else(|| span_bug!(span, "no enclosing const-continuable scope found"));
let scope = &self.scopes.const_continuable_scopes[break_index];
let state_decl = &self.local_decls[scope.state_place.as_local().unwrap()];
let state_ty = state_decl.ty;
let (discriminant_ty, rvalue) = match state_ty.kind() {
ty::Adt(adt_def, _) if adt_def.is_enum() => {
(state_ty.discriminant_ty(self.tcx), Rvalue::Discriminant(scope.state_place))
}
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => {
(state_ty, Rvalue::Use(Operand::Copy(scope.state_place)))
}
_ => span_bug!(state_decl.source_info.span, "unsupported #[loop_match] state"),
};
// The `PatCtxt` is normally used in pattern exhaustiveness checking, but reused
// here because it performs normalization and const evaluation.
let dropless_arena = rustc_arena::DroplessArena::default();
let typeck_results = self.tcx.typeck(self.def_id);
let cx = RustcPatCtxt {
tcx: self.tcx,
typeck_results,
module: self.tcx.parent_module(self.hir_id).to_def_id(),
// FIXME(#132279): We're in a body, should handle opaques.
typing_env: rustc_middle::ty::TypingEnv::non_body_analysis(self.tcx, self.def_id),
dropless_arena: &dropless_arena,
match_lint_level: self.hir_id,
whole_match_span: Some(rustc_span::Span::default()),
scrut_span: rustc_span::Span::default(),
refutable: true,
known_valid_scrutinee: true,
};
let valtree = match self.eval_unevaluated_mir_constant_to_valtree(constant) {
Ok((valtree, ty)) => {
// Defensively check that the type is monomorphic.
assert!(!ty.has_param());
valtree
}
Err(ErrorHandled::Reported(..)) => return self.cfg.start_new_block().unit(),
Err(ErrorHandled::TooGeneric(_)) => {
self.tcx.dcx().emit_fatal(ConstContinueBadConst { span: constant.span });
}
};
let Some(real_target) =
self.static_pattern_match(&cx, valtree, &*scope.arms, &scope.built_match_tree)
else {
self.tcx.dcx().emit_fatal(ConstContinueUnknownJumpTarget { span })
};
self.block_context.push(BlockFrame::SubExpr);
let state_place = scope.state_place;
block = self.expr_into_dest(state_place, block, value).into_block();
self.block_context.pop();
let discr = self.temp(discriminant_ty, source_info.span);
let scope_index = self
.scopes
.scope_index(self.scopes.const_continuable_scopes[break_index].region_scope, span);
let scope = &mut self.scopes.const_continuable_scopes[break_index];
self.cfg.push_assign(block, source_info, discr, rvalue);
let drop_and_continue_block = self.cfg.start_new_block();
let imaginary_target = self.cfg.start_new_block();
self.cfg.terminate(
block,
source_info,
TerminatorKind::FalseEdge { real_target: drop_and_continue_block, imaginary_target },
);
let drops = &mut scope.const_continue_drops;
let drop_idx = self.scopes.scopes[scope_index + 1..]
.iter()
.flat_map(|scope| &scope.drops)
.fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
drops.add_entry_point(imaginary_target, drop_idx);
self.cfg.terminate(imaginary_target, source_info, TerminatorKind::UnwindResume);
let region_scope = scope.region_scope;
let scope_index = self.scopes.scope_index(region_scope, span);
let mut drops = DropTree::new();
let drop_idx = self.scopes.scopes[scope_index + 1..]
.iter()
.flat_map(|scope| &scope.drops)
.fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
drops.add_entry_point(drop_and_continue_block, drop_idx);
// `build_drop_trees` doesn't have access to our source_info, so we
// create a dummy terminator now. `TerminatorKind::UnwindResume` is used
// because MIR type checking will panic if it hasn't been overwritten.
// (See `<ExitScopes as DropTreeBuilder>::link_entry_point`.)
self.cfg.terminate(drop_and_continue_block, source_info, TerminatorKind::UnwindResume);
self.build_exit_tree(drops, region_scope, span, Some(real_target));
return self.cfg.start_new_block().unit();
}
/// Sets up the drops for breaking from `block` due to an `if` condition
/// that turned out to be false.
///

View file

@ -465,10 +465,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::Break { .. }
| ExprKind::Closure { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. }
| ExprKind::Yield { .. }
| ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::Let { .. }
| ExprKind::Match { .. }
| ExprKind::Box { .. }

View file

@ -1149,3 +1149,80 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
}
}
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_invalid_update)]
pub(crate) struct LoopMatchInvalidUpdate {
#[primary_span]
pub lhs: Span,
#[label]
pub scrutinee: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_invalid_match)]
#[note]
pub(crate) struct LoopMatchInvalidMatch {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_unsupported_type)]
#[note]
pub(crate) struct LoopMatchUnsupportedType<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_bad_statements)]
pub(crate) struct LoopMatchBadStatements {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_bad_rhs)]
pub(crate) struct LoopMatchBadRhs {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_missing_assignment)]
pub(crate) struct LoopMatchMissingAssignment {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_loop_match_arm_with_guard)]
pub(crate) struct LoopMatchArmWithGuard {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_const_continue_bad_const)]
pub(crate) struct ConstContinueBadConst {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_const_continue_missing_value)]
pub(crate) struct ConstContinueMissingValue {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_const_continue_unknown_jump_target)]
#[note]
pub(crate) struct ConstContinueUnknownJumpTarget {
#[primary_span]
pub span: Span,
}

View file

@ -1,6 +1,7 @@
use itertools::Itertools;
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_ast::UnsafeBinderCastKind;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@ -21,6 +22,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::{Span, sym};
use tracing::{debug, info, instrument, trace};
use crate::errors::*;
use crate::thir::cx::ThirBuildCx;
impl<'tcx> ThirBuildCx<'tcx> {
@ -479,6 +481,55 @@ impl<'tcx> ThirBuildCx<'tcx> {
ExprKind::RawBorrow { mutability, arg: self.mirror_expr(arg) }
}
// Make `&pin mut $expr` and `&pin const $expr` into
// `Pin { __pointer: &mut { $expr } }` and `Pin { __pointer: &$expr }`.
hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutbl, arg_expr) => match expr_ty.kind() {
&ty::Adt(adt_def, args) if tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin) => {
let ty = args.type_at(0);
let arg_ty = self.typeck_results.expr_ty(arg_expr);
let mut arg = self.mirror_expr(arg_expr);
// For `&pin mut $place` where `$place` is not `Unpin`, move the place
// `$place` to ensure it will not be used afterwards.
if mutbl.is_mut() && !arg_ty.is_unpin(self.tcx, self.typing_env) {
let block = self.thir.blocks.push(Block {
targeted_by_break: false,
region_scope: region::Scope {
local_id: arg_expr.hir_id.local_id,
data: region::ScopeData::Node,
},
span: arg_expr.span,
stmts: Box::new([]),
expr: Some(arg),
safety_mode: BlockSafety::Safe,
});
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, arg_expr.hir_id.local_id);
arg = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty: arg_ty,
span: arg_expr.span,
kind: ExprKind::Block { block },
});
}
let expr = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty,
span: expr.span,
kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg },
});
ExprKind::Adt(Box::new(AdtExpr {
adt_def,
variant_index: FIRST_VARIANT,
args,
fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
user_ty: None,
base: AdtExprBase::None,
}))
}
_ => span_bug!(expr.span, "unexpected type for pinned borrow: {:?}", expr_ty),
},
hir::ExprKind::Block(blk, _) => ExprKind::Block { block: self.mirror_block(blk) },
hir::ExprKind::Assign(lhs, rhs, _) => {
@ -796,16 +847,38 @@ impl<'tcx> ThirBuildCx<'tcx> {
}
hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
hir::ExprKind::Break(dest, ref value) => match dest.target_id {
Ok(target_id) => ExprKind::Break {
label: region::Scope {
local_id: target_id.local_id,
data: region::ScopeData::Node,
},
value: value.map(|value| self.mirror_expr(value)),
},
Err(err) => bug!("invalid loop id for break: {}", err),
},
hir::ExprKind::Break(dest, ref value) => {
if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::ConstContinue(_)) {
match dest.target_id {
Ok(target_id) => {
let Some(value) = value else {
let span = expr.span;
self.tcx.dcx().emit_fatal(ConstContinueMissingValue { span })
};
ExprKind::ConstContinue {
label: region::Scope {
local_id: target_id.local_id,
data: region::ScopeData::Node,
},
value: self.mirror_expr(value),
}
}
Err(err) => bug!("invalid loop id for break: {}", err),
}
} else {
match dest.target_id {
Ok(target_id) => ExprKind::Break {
label: region::Scope {
local_id: target_id.local_id,
data: region::ScopeData::Node,
},
value: value.map(|value| self.mirror_expr(value)),
},
Err(err) => bug!("invalid loop id for break: {}", err),
}
}
}
hir::ExprKind::Continue(dest) => match dest.target_id {
Ok(loop_id) => ExprKind::Continue {
label: region::Scope {
@ -840,18 +913,93 @@ impl<'tcx> ThirBuildCx<'tcx> {
match_source,
},
hir::ExprKind::Loop(body, ..) => {
let block_ty = self.typeck_results.node_type(body.hir_id);
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, body.hir_id.local_id);
let block = self.mirror_block(body);
let body = self.thir.exprs.push(Expr {
ty: block_ty,
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
span: self.thir[block].span,
kind: ExprKind::Block { block },
});
ExprKind::Loop { body }
if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::LoopMatch(_)) {
let dcx = self.tcx.dcx();
// Accept either `state = expr` or `state = expr;`.
let loop_body_expr = match body.stmts {
[] => match body.expr {
Some(expr) => expr,
None => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
},
[single] if body.expr.is_none() => match single.kind {
hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
_ => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
},
[first @ last] | [first, .., last] => dcx
.emit_fatal(LoopMatchBadStatements { span: first.span.to(last.span) }),
};
let hir::ExprKind::Assign(state, rhs_expr, _) = loop_body_expr.kind else {
dcx.emit_fatal(LoopMatchMissingAssignment { span: loop_body_expr.span })
};
let hir::ExprKind::Block(block_body, _) = rhs_expr.kind else {
dcx.emit_fatal(LoopMatchBadRhs { span: rhs_expr.span })
};
// The labeled block should contain one match expression, but defining items is
// allowed.
for stmt in block_body.stmts {
if !matches!(stmt.kind, rustc_hir::StmtKind::Item(_)) {
dcx.emit_fatal(LoopMatchBadStatements { span: stmt.span })
}
}
let Some(block_body_expr) = block_body.expr else {
dcx.emit_fatal(LoopMatchBadRhs { span: block_body.span })
};
let hir::ExprKind::Match(scrutinee, arms, _match_source) = block_body_expr.kind
else {
dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
};
fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> {
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind {
if let Res::Local(hir_id) = path.res {
return Some(hir_id);
}
}
None
}
let Some(scrutinee_hir_id) = local(scrutinee) else {
dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span })
};
if local(state) != Some(scrutinee_hir_id) {
dcx.emit_fatal(LoopMatchInvalidUpdate {
scrutinee: scrutinee.span,
lhs: state.span,
})
}
ExprKind::LoopMatch {
state: self.mirror_expr(state),
region_scope: region::Scope {
local_id: block_body.hir_id.local_id,
data: region::ScopeData::Node,
},
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
match_span: block_body_expr.span,
}
} else {
let block_ty = self.typeck_results.node_type(body.hir_id);
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, body.hir_id.local_id);
let block = self.mirror_block(body);
let body = self.thir.exprs.push(Expr {
ty: block_ty,
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
span: self.thir[block].span,
kind: ExprKind::Block { block },
});
ExprKind::Loop { body }
}
}
hir::ExprKind::Field(source, ..) => ExprKind::Field {
lhs: self.mirror_expr(source),

View file

@ -331,7 +331,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
| WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
// These diverge.
Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
Become { .. }
| Break { .. }
| Continue { .. }
| ConstContinue { .. }
| Return { .. } => true,
// These are statements that evaluate to `()`.
Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
@ -353,6 +357,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
| Literal { .. }
| LogicalOp { .. }
| Loop { .. }
| LoopMatch { .. }
| Match { .. }
| NamedConst { .. }
| NonHirLiteral { .. }

View file

@ -318,6 +318,20 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_expr(*body, depth_lvl + 2);
print_indented!(self, ")", depth_lvl);
}
LoopMatch { state, region_scope, match_span, arms } => {
print_indented!(self, "LoopMatch {", depth_lvl);
print_indented!(self, "state:", depth_lvl + 1);
self.print_expr(*state, depth_lvl + 2);
print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
print_indented!(self, format!("match_span: {:?}", match_span), depth_lvl + 1);
print_indented!(self, "arms: [", depth_lvl + 1);
for arm_id in arms.iter() {
self.print_arm(*arm_id, depth_lvl + 2);
}
print_indented!(self, "]", depth_lvl + 1);
print_indented!(self, "}", depth_lvl);
}
Let { expr, pat } => {
print_indented!(self, "Let {", depth_lvl);
print_indented!(self, "expr:", depth_lvl + 1);
@ -415,6 +429,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
print_indented!(self, "}", depth_lvl);
}
ConstContinue { label, value } => {
print_indented!(self, "ConstContinue (", depth_lvl);
print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
print_indented!(self, "value:", depth_lvl + 1);
self.print_expr(*value, depth_lvl + 2);
print_indented!(self, ")", depth_lvl);
}
Return { value } => {
print_indented!(self, "Return {", depth_lvl);
print_indented!(self, "value:", depth_lvl + 1);

View file

@ -847,7 +847,7 @@ impl<'a> Parser<'a> {
self.dcx().emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span });
}
/// Parse `mut?` or `raw [ const | mut ]`.
/// Parse `mut?` or `[ raw | pin ] [ const | mut ]`.
fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {
if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) {
// `raw [ const | mut ]`.
@ -855,6 +855,11 @@ impl<'a> Parser<'a> {
assert!(found_raw);
let mutability = self.parse_const_or_mut().unwrap();
(ast::BorrowKind::Raw, mutability)
} else if let Some((ast::Pinnedness::Pinned, mutbl)) = self.parse_pin_and_mut() {
// `pin [ const | mut ]`.
// `pin` has been gated in `self.parse_pin_and_mut()` so we don't
// need to gate it here.
(ast::BorrowKind::Pin, mutbl)
} else {
// `mut?`
(ast::BorrowKind::Ref, self.parse_mutability())

View file

@ -301,6 +301,7 @@ fn emit_malformed_attribute(
| sym::naked
| sym::no_mangle
| sym::must_use
| sym::track_caller
) {
return;
}

View file

@ -82,11 +82,14 @@ passes_collapse_debuginfo =
passes_confusables = attribute should be applied to an inherent method
.label = not an inherent method
passes_const_continue_attr =
`#[const_continue]` should be applied to a break expression
.label = not a break expression
passes_const_stable_not_stable =
attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]`
.label = attribute specified here
passes_coroutine_on_non_closure =
attribute should be applied to closures
.label = not a closure
@ -446,6 +449,10 @@ passes_linkage =
attribute should be applied to a function or static
.label = not a function definition or static
passes_loop_match_attr =
`#[loop_match]` should be applied to a loop
.label = not a loop
passes_macro_export =
`#[macro_export]` only has an effect on macro definitions

View file

@ -138,6 +138,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => {
self.check_optimize(hir_id, *attr_span, span, target)
}
Attribute::Parsed(AttributeKind::LoopMatch(attr_span)) => {
self.check_loop_match(hir_id, *attr_span, target)
}
Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => {
self.check_const_continue(hir_id, *attr_span, target)
}
Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
.check_allow_internal_unstable(
hir_id,
@ -169,6 +175,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
self.check_naked(hir_id, *attr_span, span, target)
}
Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => {
self.check_track_caller(hir_id, *attr_span, attrs, span, target)
}
Attribute::Parsed(
AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
@ -205,9 +214,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.check_target_feature(hir_id, attr, span, target, attrs)
}
[sym::thread_local, ..] => self.check_thread_local(attr, span, target),
[sym::track_caller, ..] => {
self.check_track_caller(hir_id, attr.span(), attrs, span, target)
}
[sym::doc, ..] => self.check_doc_attrs(
attr,
attr_item.style,
@ -722,9 +728,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
for attr in attrs {
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "track_caller");
}
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "track_caller");
}
_ => {
self.dcx().emit_err(errors::TrackedCallerWrongLocation {
@ -2633,6 +2637,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
}
fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) {
let node_span = self.tcx.hir_span(hir_id);
if !matches!(target, Target::Expression) {
self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
return;
}
if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Loop(..)) {
self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
};
}
fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) {
let node_span = self.tcx.hir_span(hir_id);
if !matches!(target, Target::Expression) {
self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
return;
}
if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Break(..)) {
self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
};
}
}
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {

View file

@ -31,6 +31,24 @@ pub(crate) struct AutoDiffAttr {
pub attr_span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_loop_match_attr)]
pub(crate) struct LoopMatchAttr {
#[primary_span]
pub attr_span: Span,
#[label]
pub node_span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_const_continue_attr)]
pub(crate) struct ConstContinueAttr {
#[primary_span]
pub attr_span: Span,
#[label]
pub node_span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_outer_crate_level_attr)]
pub(crate) struct OuterCrateLevelAttr;

View file

@ -314,7 +314,8 @@ impl IntRange {
IntRange { lo, hi }
}
fn is_subrange(&self, other: &Self) -> bool {
#[inline]
pub fn is_subrange(&self, other: &Self) -> bool {
other.lo <= self.lo && self.hi <= other.hi
}

View file

@ -494,6 +494,7 @@ impl RustcInternal for Abi {
Abi::RustCall => rustc_abi::ExternAbi::RustCall,
Abi::Unadjusted => rustc_abi::ExternAbi::Unadjusted,
Abi::RustCold => rustc_abi::ExternAbi::RustCold,
Abi::RustInvalid => rustc_abi::ExternAbi::RustInvalid,
Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
Abi::Custom => rustc_abi::ExternAbi::Custom,

View file

@ -877,6 +877,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
ExternAbi::RustCall => Abi::RustCall,
ExternAbi::Unadjusted => Abi::Unadjusted,
ExternAbi::RustCold => Abi::RustCold,
ExternAbi::RustInvalid => Abi::RustInvalid,
ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
ExternAbi::Custom => Abi::Custom,

View file

@ -1126,6 +1126,7 @@ pub enum Abi {
RustCold,
RiscvInterruptM,
RiscvInterruptS,
RustInvalid,
Custom,
}

View file

@ -693,6 +693,7 @@ symbols! {
const_closures,
const_compare_raw_pointers,
const_constructor,
const_continue,
const_deallocate,
const_destruct,
const_eval_limit,
@ -1305,6 +1306,7 @@ symbols! {
logf64,
loongarch_target_feature,
loop_break_value,
loop_match,
lt,
m68k_target_feature,
macro_at_most_once_rep,

View file

@ -156,7 +156,8 @@ impl AbiMap {
| ExternAbi::Msp430Interrupt
| ExternAbi::RiscvInterruptM
| ExternAbi::RiscvInterruptS
| ExternAbi::X86Interrupt,
| ExternAbi::X86Interrupt
| ExternAbi::RustInvalid,
_,
) => return AbiMapping::Invalid,
};

View file

@ -226,7 +226,11 @@ fn recurse_build<'tcx>(
ExprKind::Yield { .. } => {
error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
}
ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Break { .. }
| ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. } => {
error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
}
ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?,
@ -329,6 +333,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::NeverToAny { .. }
| thir::ExprKind::PointerCoercion { .. }
| thir::ExprKind::Loop { .. }
| thir::ExprKind::LoopMatch { .. }
| thir::ExprKind::Let { .. }
| thir::ExprKind::Match { .. }
| thir::ExprKind::Block { .. }
@ -342,6 +347,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::RawBorrow { .. }
| thir::ExprKind::Break { .. }
| thir::ExprKind::Continue { .. }
| thir::ExprKind::ConstContinue { .. }
| thir::ExprKind::Return { .. }
| thir::ExprKind::Become { .. }
| thir::ExprKind::Array { .. }

View file

@ -467,7 +467,7 @@ impl CStr {
/// // 💀 this violates `CStr::from_ptr`'s safety contract
/// // 💀 leading to a dereference of a dangling pointer,
/// // 💀 which is immediate undefined behavior.
/// // 💀 *BOOM*, you're dead, you're entire program has no meaning.
/// // 💀 *BOOM*, you're dead, your entire program has no meaning.
/// dbg!(unsafe { CStr::from_ptr(ptr) });
/// ```
///

View file

@ -132,6 +132,7 @@ impl Iterator for ReadDir {
let mut wfd = mem::zeroed();
loop {
if c::FindNextFileW(handle.0, &mut wfd) == 0 {
self.handle = None;
match api::get_last_error() {
WinError::NO_MORE_FILES => return None,
WinError { code } => {

View file

@ -10,12 +10,12 @@ generated code is normally invisible to the programmer.
This flag helps identify such cases. When enabled, the compiler measures the
effect on code size of all used macros and prints a table summarizing that
effect. For each distinct macro, it counts how many times it is used, and the
net effect on code size (in terms of lines of code, and bytes of code). The
effect. For each distinct macro, it counts how many times it is used, and how
much code it produces when expanded (in lines of code, and bytes of code). The
code size evaluation uses the compiler's internal pretty-printing, and so will
be independent of the formatting in the original code.
Note that the net effect of a macro may be negative. E.g. the `cfg!` and
Note that the output size of a macro may be zero. E.g. the `cfg!` and
`#[test]` macros often strip out code.
If a macro is identified as causing a large increase in code size, it is worth

View file

@ -0,0 +1,52 @@
# `loop_match`
The tracking issue for this feature is: [#132306]
[#132306]: https://github.com/rust-lang/rust/issues/132306
------
The `#[loop_match]` and `#[const_continue]` attributes can be used to improve the code
generation of logic that fits this shape:
```ignore (pseudo-rust)
loop {
state = 'blk: {
match state {
State::A => {
break 'blk State::B
}
State::B => { /* ... */ }
/* ... */
}
}
}
```
Here the loop itself can be annotated with `#[loop_match]`, and any `break 'blk` with
`#[const_continue]` if the value is know at compile time:
```ignore (pseudo-rust)
#[loop_match]
loop {
state = 'blk: {
match state {
State::A => {
#[const_continue]
break 'blk State::B
}
State::B => { /* ... */ }
/* ... */
}
}
}
```
The observable behavior of this loop is exactly the same as without the extra attributes.
The difference is in the generated output: normally, when the state is `A`, control flow
moves from the `A` branch, back to the top of the loop, then to the `B` branch. With the
attributes, The `A` branch will immediately jump to the `B` branch.
Removing the indirection can be beneficial for stack usage and branch prediction, and
enables other optimizations by clearly splitting out the control flow paths that your
program will actually use.

View file

@ -469,7 +469,8 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
let unsafety_flag = match myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
if myitem.fn_header(tcx).unwrap().is_unsafe() =>
if myitem.fn_header(tcx).unwrap().safety
== hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
{
"<sup title=\"unsafe function\">⚠</sup>"
}

@ -1 +1 @@
Subproject commit 84709f085062cbf3c51fa507527c1b2334015178
Subproject commit 409fed7dc1553d49cb9a8c0637d12d65571346ce

View file

@ -7,6 +7,7 @@ use clippy_utils::{
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
};
use rustc_abi::ExternAbi;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
@ -155,7 +156,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
let sig = match callee_ty_adjusted.kind() {
ty::FnDef(def, _) => {
// Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
if cx.tcx.has_attr(*def, sym::track_caller) {
if find_attr!(cx.tcx.get_all_attrs(*def), AttributeKind::TrackCaller(..)) {
return;
}
@ -236,7 +237,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
},
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
&& !cx.tcx.has_attr(method_def_id, sym::track_caller)
&& !find_attr!(cx.tcx.get_all_attrs(method_def_id), AttributeKind::TrackCaller(..))
&& check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
{
let mut app = Applicability::MachineApplicable;

View file

@ -2289,8 +2289,10 @@ fn rewrite_expr_addrof(
) -> RewriteResult {
let operator_str = match (mutability, borrow_kind) {
(ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
(ast::Mutability::Not, ast::BorrowKind::Pin) => "&pin const ",
(ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
(ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ",
(ast::Mutability::Mut, ast::BorrowKind::Pin) => "&pin mut ",
(ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ",
};
rewrite_unary_prefix(context, operator_str, expr, shape)

View file

@ -18,3 +18,13 @@ impl Foo {
mut self) {}
fn i(&pin mut self) {}
}
fn borrows() {
let mut foo = 0_i32;
let x: Pin<&mut _> = & pin
mut foo;
let x: Pin<&_> = &
pin const
foo;
}

View file

@ -16,3 +16,10 @@ impl Foo {
fn h<'a>(&'a pin mut self) {}
fn i(&pin mut self) {}
}
fn borrows() {
let mut foo = 0_i32;
let x: Pin<&mut _> = &pin mut foo;
let x: Pin<&_> = &pin const foo;
}

View file

@ -374,6 +374,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"scoped-tls",
"scopeguard",
"self_cell",
"semver",
"serde",
"serde_derive",
"serde_json",

View file

@ -10,4 +10,4 @@ name = "wasm-component-ld"
path = "src/main.rs"
[dependencies]
wasm-component-ld = "0.5.13"
wasm-component-ld = "0.5.14"

View file

@ -13,8 +13,6 @@
extern crate minicore;
use minicore::*;
// CHECK: define x86_intrcc i64 @has_x86_interrupt_abi
// CHECK: define x86_intrcc void @has_x86_interrupt_abi
#[no_mangle]
pub extern "x86-interrupt" fn has_x86_interrupt_abi(a: i64) -> i64 {
a
}
pub extern "x86-interrupt" fn has_x86_interrupt_abi() {}

View file

@ -55,3 +55,48 @@ pub fn ptr_to_int(p: *mut u16) -> usize {
pub fn int_to_ptr(i: usize) -> *mut u16 {
unsafe { std::mem::transmute(i) }
}
// This is the one case where signedness matters to transmuting:
// the LLVM type is `i8` here because of `repr(i8)`,
// whereas below with the `repr(u8)` it's `i1` in LLVM instead.
#[repr(i8)]
pub enum FakeBoolSigned {
False = 0,
True = 1,
}
// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1 zeroext %b)
// CHECK: %_0 = zext i1 %b to i8
// CHECK-NEXT: ret i8 %_0
#[no_mangle]
pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned {
unsafe { std::mem::transmute(b) }
}
// CHECK-LABEL: define{{.*}}i1 @fake_bool_signed_to_bool(i8 %b)
// CHECK: %_0 = trunc nuw i8 %b to i1
// CHECK-NEXT: ret i1 %_0
#[no_mangle]
pub fn fake_bool_signed_to_bool(b: FakeBoolSigned) -> bool {
unsafe { std::mem::transmute(b) }
}
#[repr(u8)]
pub enum FakeBoolUnsigned {
False = 0,
True = 1,
}
// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1 zeroext %b)
// CHECK: ret i1 %b
#[no_mangle]
pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned {
unsafe { std::mem::transmute(b) }
}
// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b)
// CHECK: ret i1 %b
#[no_mangle]
pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool {
unsafe { std::mem::transmute(b) }
}

View file

@ -0,0 +1,44 @@
//@ pretty-compare-only
//@ pretty-mode:hir
//@ pp-exact:pin-ergonomics-hir.pp
#![feature(pin_ergonomics)]
#![allow(dead_code, incomplete_features)]
#[prelude_import]
use ::std::prelude::rust_2015::*;
#[macro_use]
extern crate std;
use std::pin::Pin;
struct Foo;
impl Foo {
fn baz(&mut self) { }
fn baz_const(&self) { }
fn baz_lt<'a>(&mut self) { }
fn baz_const_lt(&self) { }
}
fn foo(_: Pin<&'_ mut Foo>) { }
fn foo_lt<'a>(_: Pin<&'a mut Foo>) { }
fn foo_const(_: Pin<&'_ Foo>) { }
fn foo_const_lt(_: Pin<&'_ Foo>) { }
fn bar() {
let mut x: Pin<&mut _> = &pin mut Foo;
foo(x.as_mut());
foo(x.as_mut());
foo_const(x);
let x: Pin<&_> = &pin const Foo;
foo_const(x);
foo_const(x);
}
fn main() { }

View file

@ -0,0 +1,40 @@
//@ pretty-compare-only
//@ pretty-mode:hir
//@ pp-exact:pin-ergonomics-hir.pp
#![feature(pin_ergonomics)]
#![allow(dead_code, incomplete_features)]
use std::pin::Pin;
struct Foo;
impl Foo {
fn baz(&mut self) { }
fn baz_const(&self) { }
fn baz_lt<'a>(&mut self) { }
fn baz_const_lt(&self) { }
}
fn foo(_: Pin<&'_ mut Foo>) { }
fn foo_lt<'a>(_: Pin<&'a mut Foo>) { }
fn foo_const(_: Pin<&'_ Foo>) { }
fn foo_const_lt(_: Pin<&'_ Foo>) { }
fn bar() {
let mut x: Pin<&mut _> = &pin mut Foo;
foo(x.as_mut());
foo(x.as_mut());
foo_const(x);
let x: Pin<&_> = &pin const Foo;
foo_const(x);
foo_const(x);
}
fn main() { }

View file

@ -3,6 +3,8 @@
#![feature(pin_ergonomics)]
#![allow(dead_code, incomplete_features)]
use std::pin::Pin;
struct Foo;
impl Foo {
@ -21,4 +23,15 @@ fn foo_lt<'a>(_: &'a pin mut Foo) {}
fn foo_const(_: &pin const Foo) {}
fn foo_const_lt(_: &'_ pin const Foo) {}
fn bar() {
let mut x: Pin<&mut _> = &pin mut Foo;
foo(x.as_mut());
foo(x.as_mut());
foo_const(x);
let x: Pin<&_> = &pin const Foo;
foo_const(x);
foo_const(x);
}
fn main() {}

View file

@ -12,7 +12,15 @@ fn panic(_: &PanicInfo) -> ! {
}
#[lang = "eh_personality"]
fn eh() {}
fn eh(
_version: i32,
_actions: i32,
_exception_class: u64,
_exception_object: *mut (),
_context: *mut (),
) -> i32 {
loop {}
}
#[alloc_error_handler]
fn oom(_: Layout) -> ! {

View file

@ -21,6 +21,12 @@ extern "C" fn __rust_foreign_exception() -> ! {
}
#[lang = "eh_personality"]
fn eh_personality() {
fn eh_personality(
_version: i32,
_actions: i32,
_exception_class: u64,
_exception_object: *mut (),
_context: *mut (),
) -> i32 {
loop {}
}

View file

@ -57,7 +57,8 @@ fn main() {
diff()
.expected_file("short-error.txt")
.actual_text("(linker error)", out.stderr())
.normalize(r#"/rustc[^/]*/"#, "/rustc/")
.normalize(r#"/rustc[^/_-]*/"#, "/rustc/")
.normalize("libpanic_abort", "libpanic_unwind")
.normalize(
regex::escape(run_make_support::build_root().to_str().unwrap()),
"/build-root",

View file

@ -12,7 +12,13 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! {
}
#[no_mangle]
extern "C" fn rust_eh_personality() {
extern "C" fn rust_eh_personality(
_version: i32,
_actions: i32,
_exception_class: u64,
_exception_object: *mut (),
_context: *mut (),
) -> i32 {
loop {}
}

View file

@ -5,8 +5,9 @@
// See https://github.com/rust-lang/rust/issues/107910
//@ needs-target-std
//@ ignore-windows
// Reason: the assert_eq! on line 32 fails, as error output on Windows is different.
//@ ignore-i686-pc-windows-msvc
// Reason: the assert_eq! on line 37 fails, almost seems like it missing debug info?
// Haven't been able to reproduce locally, but it happens on CI.
use run_make_support::rustc;
@ -29,10 +30,16 @@ fn main() {
let rustc_query_count_full = count_lines_with(rust_test_log_2, "rustc_query_");
assert!(rust_test_log_1.lines().count() < rust_test_log_2.lines().count());
assert!(
rust_test_log_1.lines().count() < rust_test_log_2.lines().count(),
"Short backtrace should be shorter than full backtrace.\nShort backtrace:\n\
{rust_test_log_1}\nFull backtrace:\n{rust_test_log_2}"
);
assert_eq!(
count_lines_with(rust_test_log_2, "__rust_begin_short_backtrace"),
count_lines_with(rust_test_log_2, "__rust_end_short_backtrace")
count_lines_with(rust_test_log_2, "__rust_end_short_backtrace"),
"Full backtrace should contain the short backtrace markers.\nFull backtrace:\n\
{rust_test_log_2}"
);
assert!(count_lines_with(rust_test_log_1, "rustc_query_") + 5 < rustc_query_count_full);
assert!(rustc_query_count_full > 5);

View file

@ -0,0 +1,38 @@
#![crate_name = "foo"]
//@ has 'foo/index.html'
//@ has - '//dl[@class="item-table"]/dt[1]//a' 'f1_safe'
//@ has - '//dl[@class="item-table"]/dt[1]//code' 'popcnt'
//@ count - '//dl[@class="item-table"]/dt[1]//sup' 0
//@ has - '//dl[@class="item-table"]/dt[2]//a' 'f2_not_safe'
//@ has - '//dl[@class="item-table"]/dt[2]//code' 'avx2'
//@ count - '//dl[@class="item-table"]/dt[2]//sup' 1
//@ has - '//dl[@class="item-table"]/dt[2]//sup' '⚠'
#[target_feature(enable = "popcnt")]
//@ has 'foo/fn.f1_safe.html'
//@ matches - '//pre[@class="rust item-decl"]' '^pub fn f1_safe'
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available with target feature popcnt only.'
pub fn f1_safe() {}
//@ has 'foo/fn.f2_not_safe.html'
//@ matches - '//pre[@class="rust item-decl"]' '^pub unsafe fn f2_not_safe()'
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available with target feature avx2 only.'
#[target_feature(enable = "avx2")]
pub unsafe fn f2_not_safe() {}
//@ has 'foo/fn.f3_multifeatures_in_attr.html'
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on target features popcnt and avx2 only.'
#[target_feature(enable = "popcnt", enable = "avx2")]
pub fn f3_multifeatures_in_attr() {}
//@ has 'foo/fn.f4_multi_attrs.html'
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on target features popcnt and avx2 only.'
#[target_feature(enable = "popcnt")]
#[target_feature(enable = "avx2")]
pub fn f4_multi_attrs() {}

View file

@ -5,7 +5,7 @@
#[unsafe(naked)]
extern "custom" fn must_be_unsafe(a: i64) -> i64 {
//~^ ERROR functions with the `"custom"` ABI must be unsafe
//~^ ERROR functions with the "custom" ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
std::arch::naked_asm!("")
}
@ -23,7 +23,7 @@ unsafe extern "custom" fn no_return_type() -> i64 {
}
unsafe extern "custom" fn double(a: i64) -> i64 {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR invalid signature for `extern "custom"` function
unimplemented!()
}
@ -32,7 +32,7 @@ struct Thing(i64);
impl Thing {
unsafe extern "custom" fn is_even(self) -> bool {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR invalid signature for `extern "custom"` function
unimplemented!()
}
@ -40,7 +40,7 @@ impl Thing {
trait BitwiseNot {
unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR invalid signature for `extern "custom"` function
unimplemented!()
}
@ -50,14 +50,14 @@ impl BitwiseNot for Thing {}
trait Negate {
extern "custom" fn negate(a: i64) -> i64;
//~^ ERROR functions with the `"custom"` ABI must be unsafe
//~^ ERROR functions with the "custom" ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
}
impl Negate for Thing {
extern "custom" fn negate(a: i64) -> i64 {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the `"custom"` ABI must be unsafe
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the "custom" ABI must be unsafe
//~| ERROR invalid signature for `extern "custom"` function
-a
}
@ -68,7 +68,7 @@ unsafe extern "custom" {
//~^ ERROR invalid signature for `extern "custom"` function
safe fn extern_cannot_be_safe();
//~^ ERROR foreign functions with the `"custom"` ABI cannot be safe
//~^ ERROR foreign functions with the "custom" ABI cannot be safe
}
fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
@ -95,8 +95,8 @@ const unsafe extern "custom" fn no_const_fn() {
}
async unsafe extern "custom" fn no_async_fn() {
//~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the `"custom"` ABI cannot be `async`
//~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
//~| ERROR functions with the "custom" ABI cannot be `async`
}
fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() {

View file

@ -1,4 +1,4 @@
error: functions with the `"custom"` ABI must be unsafe
error: functions with the "custom" ABI must be unsafe
--> $DIR/bad-custom.rs:7:1
|
LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
@ -15,7 +15,7 @@ error: invalid signature for `extern "custom"` function
LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - extern "custom" fn must_be_unsafe(a: i64) -> i64 {
@ -28,7 +28,7 @@ error: invalid signature for `extern "custom"` function
LL | unsafe extern "custom" fn no_parameters(a: i64) {
| ^^^^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn no_parameters(a: i64) {
@ -41,7 +41,7 @@ error: invalid signature for `extern "custom"` function
LL | unsafe extern "custom" fn no_return_type() -> i64 {
| ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn no_return_type() -> i64 {
@ -54,7 +54,7 @@ error: invalid signature for `extern "custom"` function
LL | unsafe extern "custom" fn double(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn double(a: i64) -> i64 {
@ -67,7 +67,7 @@ error: invalid signature for `extern "custom"` function
LL | unsafe extern "custom" fn is_even(self) -> bool {
| ^^^^ ^^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn is_even(self) -> bool {
@ -80,14 +80,14 @@ error: invalid signature for `extern "custom"` function
LL | unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
LL + unsafe extern "custom" fn bitwise_not() {
|
error: functions with the `"custom"` ABI must be unsafe
error: functions with the "custom" ABI must be unsafe
--> $DIR/bad-custom.rs:52:5
|
LL | extern "custom" fn negate(a: i64) -> i64;
@ -104,14 +104,14 @@ error: invalid signature for `extern "custom"` function
LL | extern "custom" fn negate(a: i64) -> i64;
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - extern "custom" fn negate(a: i64) -> i64;
LL + extern "custom" fn negate();
|
error: functions with the `"custom"` ABI must be unsafe
error: functions with the "custom" ABI must be unsafe
--> $DIR/bad-custom.rs:58:5
|
LL | extern "custom" fn negate(a: i64) -> i64 {
@ -128,7 +128,7 @@ error: invalid signature for `extern "custom"` function
LL | extern "custom" fn negate(a: i64) -> i64 {
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - extern "custom" fn negate(a: i64) -> i64 {
@ -141,14 +141,14 @@ error: invalid signature for `extern "custom"` function
LL | fn increment(a: i64) -> i64;
| ^^^^^^ ^^^
|
= note: functions with the `"custom"` ABI cannot have any parameters or return type
= note: functions with the "custom" ABI cannot have any parameters or return type
help: remove the parameters and return type
|
LL - fn increment(a: i64) -> i64;
LL + fn increment();
|
error: foreign functions with the `"custom"` ABI cannot be safe
error: foreign functions with the "custom" ABI cannot be safe
--> $DIR/bad-custom.rs:70:5
|
LL | safe fn extern_cannot_be_safe();
@ -160,7 +160,7 @@ LL - safe fn extern_cannot_be_safe();
LL + fn extern_cannot_be_safe();
|
error: functions with the `"custom"` ABI cannot be `async`
error: functions with the "custom" ABI cannot be `async`
--> $DIR/bad-custom.rs:97:1
|
LL | async unsafe extern "custom" fn no_async_fn() {
@ -172,7 +172,7 @@ LL - async unsafe extern "custom" fn no_async_fn() {
LL + unsafe extern "custom" fn no_async_fn() {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
error: items with the "custom" ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:97:1
|
LL | async unsafe extern "custom" fn no_async_fn() {
@ -197,7 +197,7 @@ LL | f
= note: unsafe function cannot be called generically without an unsafe block
= note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
error: items with the "custom" ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:25:1
|
LL | unsafe extern "custom" fn double(a: i64) -> i64 {
@ -209,7 +209,7 @@ LL + #[unsafe(naked)]
LL | unsafe extern "custom" fn double(a: i64) -> i64 {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
error: items with the "custom" ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:34:5
|
LL | unsafe extern "custom" fn is_even(self) -> bool {
@ -221,7 +221,7 @@ LL + #[unsafe(naked)]
LL | unsafe extern "custom" fn is_even(self) -> bool {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
error: items with the "custom" ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:42:5
|
LL | unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
@ -233,7 +233,7 @@ LL + #[unsafe(naked)]
LL | unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
|
error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
error: items with the "custom" ABI can only be declared externally or defined via naked functions
--> $DIR/bad-custom.rs:58:5
|
LL | extern "custom" fn negate(a: i64) -> i64 {

View file

@ -1,71 +1,71 @@
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:38:8
--> $DIR/cannot-be-called.rs:37:8
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:42:8
--> $DIR/cannot-be-called.rs:41:8
|
LL | extern "riscv-interrupt-m" fn riscv_m() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:44:8
--> $DIR/cannot-be-called.rs:43:8
|
LL | extern "riscv-interrupt-s" fn riscv_s() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:46:8
--> $DIR/cannot-be-called.rs:45:8
|
LL | extern "x86-interrupt" fn x86() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:65:25
--> $DIR/cannot-be-called.rs:70:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:77:26
--> $DIR/cannot-be-called.rs:76:26
|
LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:83:26
--> $DIR/cannot-be-called.rs:82:26
|
LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:89:22
--> $DIR/cannot-be-called.rs:88:22
|
LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error: functions with the "avr-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:53:5
--> $DIR/cannot-be-called.rs:50:5
|
LL | avr();
| ^^^^^
|
note: an `extern "avr-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:53:5
--> $DIR/cannot-be-called.rs:50:5
|
LL | avr();
| ^^^^^
error: functions with the "avr-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:73:5
--> $DIR/cannot-be-called.rs:66:5
|
LL | f()
| ^^^
|
note: an `extern "avr-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:73:5
--> $DIR/cannot-be-called.rs:66:5
|
LL | f()
| ^^^

View file

@ -1,71 +1,71 @@
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:38:8
--> $DIR/cannot-be-called.rs:37:8
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:40:8
--> $DIR/cannot-be-called.rs:39:8
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:42:8
--> $DIR/cannot-be-called.rs:41:8
|
LL | extern "riscv-interrupt-m" fn riscv_m() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:44:8
--> $DIR/cannot-be-called.rs:43:8
|
LL | extern "riscv-interrupt-s" fn riscv_s() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:65:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:71:22
--> $DIR/cannot-be-called.rs:64:22
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:70:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:77:26
--> $DIR/cannot-be-called.rs:76:26
|
LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:83:26
--> $DIR/cannot-be-called.rs:82:26
|
LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error: functions with the "x86-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:59:5
--> $DIR/cannot-be-called.rs:58:5
|
LL | x86();
| ^^^^^
|
note: an `extern "x86-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:59:5
--> $DIR/cannot-be-called.rs:58:5
|
LL | x86();
| ^^^^^
error: functions with the "x86-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:91:5
--> $DIR/cannot-be-called.rs:90:5
|
LL | f()
| ^^^
|
note: an `extern "x86-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:91:5
--> $DIR/cannot-be-called.rs:90:5
|
LL | f()
| ^^^

View file

@ -1,71 +1,71 @@
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:40:8
--> $DIR/cannot-be-called.rs:39:8
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:42:8
--> $DIR/cannot-be-called.rs:41:8
|
LL | extern "riscv-interrupt-m" fn riscv_m() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:44:8
--> $DIR/cannot-be-called.rs:43:8
|
LL | extern "riscv-interrupt-s" fn riscv_s() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:46:8
--> $DIR/cannot-be-called.rs:45:8
|
LL | extern "x86-interrupt" fn x86() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:71:22
--> $DIR/cannot-be-called.rs:64:22
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:77:26
--> $DIR/cannot-be-called.rs:76:26
|
LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:83:26
--> $DIR/cannot-be-called.rs:82:26
|
LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:89:22
--> $DIR/cannot-be-called.rs:88:22
|
LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error: functions with the "msp430-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:51:5
--> $DIR/cannot-be-called.rs:52:5
|
LL | msp430();
| ^^^^^^^^
|
note: an `extern "msp430-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:51:5
--> $DIR/cannot-be-called.rs:52:5
|
LL | msp430();
| ^^^^^^^^
error: functions with the "msp430-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:67:5
--> $DIR/cannot-be-called.rs:72:5
|
LL | f()
| ^^^
|
note: an `extern "msp430-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:67:5
--> $DIR/cannot-be-called.rs:72:5
|
LL | f()
| ^^^

View file

@ -1,83 +1,83 @@
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:38:8
--> $DIR/cannot-be-called.rs:37:8
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:40:8
--> $DIR/cannot-be-called.rs:39:8
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:46:8
--> $DIR/cannot-be-called.rs:45:8
|
LL | extern "x86-interrupt" fn x86() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:65:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:71:22
--> $DIR/cannot-be-called.rs:64:22
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:70:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:89:22
--> $DIR/cannot-be-called.rs:88:22
|
LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error: functions with the "riscv-interrupt-m" ABI cannot be called
--> $DIR/cannot-be-called.rs:55:5
--> $DIR/cannot-be-called.rs:54:5
|
LL | riscv_m();
| ^^^^^^^^^
|
note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:55:5
--> $DIR/cannot-be-called.rs:54:5
|
LL | riscv_m();
| ^^^^^^^^^
error: functions with the "riscv-interrupt-s" ABI cannot be called
--> $DIR/cannot-be-called.rs:57:5
--> $DIR/cannot-be-called.rs:56:5
|
LL | riscv_s();
| ^^^^^^^^^
|
note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:57:5
--> $DIR/cannot-be-called.rs:56:5
|
LL | riscv_s();
| ^^^^^^^^^
error: functions with the "riscv-interrupt-m" ABI cannot be called
--> $DIR/cannot-be-called.rs:79:5
--> $DIR/cannot-be-called.rs:78:5
|
LL | f()
| ^^^
|
note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:79:5
--> $DIR/cannot-be-called.rs:78:5
|
LL | f()
| ^^^
error: functions with the "riscv-interrupt-s" ABI cannot be called
--> $DIR/cannot-be-called.rs:85:5
--> $DIR/cannot-be-called.rs:84:5
|
LL | f()
| ^^^
|
note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:85:5
--> $DIR/cannot-be-called.rs:84:5
|
LL | f()
| ^^^

View file

@ -1,83 +1,83 @@
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:38:8
--> $DIR/cannot-be-called.rs:37:8
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:40:8
--> $DIR/cannot-be-called.rs:39:8
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:46:8
--> $DIR/cannot-be-called.rs:45:8
|
LL | extern "x86-interrupt" fn x86() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:65:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:71:22
--> $DIR/cannot-be-called.rs:64:22
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:70:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "x86-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:89:22
--> $DIR/cannot-be-called.rs:88:22
|
LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error: functions with the "riscv-interrupt-m" ABI cannot be called
--> $DIR/cannot-be-called.rs:55:5
--> $DIR/cannot-be-called.rs:54:5
|
LL | riscv_m();
| ^^^^^^^^^
|
note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:55:5
--> $DIR/cannot-be-called.rs:54:5
|
LL | riscv_m();
| ^^^^^^^^^
error: functions with the "riscv-interrupt-s" ABI cannot be called
--> $DIR/cannot-be-called.rs:57:5
--> $DIR/cannot-be-called.rs:56:5
|
LL | riscv_s();
| ^^^^^^^^^
|
note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:57:5
--> $DIR/cannot-be-called.rs:56:5
|
LL | riscv_s();
| ^^^^^^^^^
error: functions with the "riscv-interrupt-m" ABI cannot be called
--> $DIR/cannot-be-called.rs:79:5
--> $DIR/cannot-be-called.rs:78:5
|
LL | f()
| ^^^
|
note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:79:5
--> $DIR/cannot-be-called.rs:78:5
|
LL | f()
| ^^^
error: functions with the "riscv-interrupt-s" ABI cannot be called
--> $DIR/cannot-be-called.rs:85:5
--> $DIR/cannot-be-called.rs:84:5
|
LL | f()
| ^^^
|
note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:85:5
--> $DIR/cannot-be-called.rs:84:5
|
LL | f()
| ^^^

View file

@ -25,9 +25,8 @@ So we test that they error in essentially all of the same places.
no_core,
abi_msp430_interrupt,
abi_avr_interrupt,
abi_gpu_kernel,
abi_x86_interrupt,
abi_riscv_interrupt,
abi_riscv_interrupt
)]
extern crate minicore;
@ -48,10 +47,10 @@ extern "x86-interrupt" fn x86() {}
/* extern "interrupt" calls */
fn call_the_interrupts() {
msp430();
//[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
avr();
//[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be called
msp430();
//[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
riscv_m();
//[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be called
riscv_s();
@ -62,18 +61,18 @@ fn call_the_interrupts() {
/* extern "interrupt" fnptr calls */
fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
//[x64,x64_win,i686,riscv32,riscv64,avr]~^ ERROR is not a supported ABI
f()
//[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
}
fn avr_ptr(f: extern "avr-interrupt" fn()) {
//[x64,x64_win,i686,riscv32,riscv64,msp430]~^ ERROR is not a supported ABI
f()
//[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be called
}
fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
//[x64,x64_win,i686,riscv32,riscv64,avr]~^ ERROR is not a supported ABI
f()
//[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
}
fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
//[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI
f()

View file

@ -1,71 +1,71 @@
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:38:8
--> $DIR/cannot-be-called.rs:37:8
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:40:8
--> $DIR/cannot-be-called.rs:39:8
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:42:8
--> $DIR/cannot-be-called.rs:41:8
|
LL | extern "riscv-interrupt-m" fn riscv_m() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:44:8
--> $DIR/cannot-be-called.rs:43:8
|
LL | extern "riscv-interrupt-s" fn riscv_s() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:65:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:71:22
--> $DIR/cannot-be-called.rs:64:22
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:70:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:77:26
--> $DIR/cannot-be-called.rs:76:26
|
LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:83:26
--> $DIR/cannot-be-called.rs:82:26
|
LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error: functions with the "x86-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:59:5
--> $DIR/cannot-be-called.rs:58:5
|
LL | x86();
| ^^^^^
|
note: an `extern "x86-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:59:5
--> $DIR/cannot-be-called.rs:58:5
|
LL | x86();
| ^^^^^
error: functions with the "x86-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:91:5
--> $DIR/cannot-be-called.rs:90:5
|
LL | f()
| ^^^
|
note: an `extern "x86-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:91:5
--> $DIR/cannot-be-called.rs:90:5
|
LL | f()
| ^^^

View file

@ -1,71 +1,71 @@
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:38:8
--> $DIR/cannot-be-called.rs:37:8
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:40:8
--> $DIR/cannot-be-called.rs:39:8
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:42:8
--> $DIR/cannot-be-called.rs:41:8
|
LL | extern "riscv-interrupt-m" fn riscv_m() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:44:8
--> $DIR/cannot-be-called.rs:43:8
|
LL | extern "riscv-interrupt-s" fn riscv_s() {}
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:65:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "avr-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:71:22
--> $DIR/cannot-be-called.rs:64:22
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^
error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:70:25
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:77:26
--> $DIR/cannot-be-called.rs:76:26
|
LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
--> $DIR/cannot-be-called.rs:83:26
--> $DIR/cannot-be-called.rs:82:26
|
LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
| ^^^^^^^^^^^^^^^^^^^
error: functions with the "x86-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:59:5
--> $DIR/cannot-be-called.rs:58:5
|
LL | x86();
| ^^^^^
|
note: an `extern "x86-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:59:5
--> $DIR/cannot-be-called.rs:58:5
|
LL | x86();
| ^^^^^
error: functions with the "x86-interrupt" ABI cannot be called
--> $DIR/cannot-be-called.rs:91:5
--> $DIR/cannot-be-called.rs:90:5
|
LL | f()
| ^^^
|
note: an `extern "x86-interrupt"` function can only be called using inline assembly
--> $DIR/cannot-be-called.rs:91:5
--> $DIR/cannot-be-called.rs:90:5
|
LL | f()
| ^^^

View file

@ -0,0 +1,23 @@
error: functions with the "avr-interrupt" ABI cannot be `async`
--> $DIR/cannot-be-coroutine.rs:36:1
|
LL | async extern "avr-interrupt" fn avr() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `async` keyword from this definiton
|
LL - async extern "avr-interrupt" fn avr() {
LL + extern "avr-interrupt" fn avr() {
|
error: requires `ResumeTy` lang_item
--> $DIR/cannot-be-coroutine.rs:32:19
|
LL | async fn vanilla(){
| ___________________^
LL | |
LL | | }
| |_^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,23 @@
error: functions with the "x86-interrupt" ABI cannot be `async`
--> $DIR/cannot-be-coroutine.rs:52:1
|
LL | async extern "x86-interrupt" fn x86() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `async` keyword from this definiton
|
LL - async extern "x86-interrupt" fn x86() {
LL + extern "x86-interrupt" fn x86() {
|
error: requires `ResumeTy` lang_item
--> $DIR/cannot-be-coroutine.rs:32:19
|
LL | async fn vanilla(){
| ___________________^
LL | |
LL | | }
| |_^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,23 @@
error: functions with the "msp430-interrupt" ABI cannot be `async`
--> $DIR/cannot-be-coroutine.rs:40:1
|
LL | async extern "msp430-interrupt" fn msp430() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `async` keyword from this definiton
|
LL - async extern "msp430-interrupt" fn msp430() {
LL + extern "msp430-interrupt" fn msp430() {
|
error: requires `ResumeTy` lang_item
--> $DIR/cannot-be-coroutine.rs:32:19
|
LL | async fn vanilla(){
| ___________________^
LL | |
LL | | }
| |_^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,35 @@
error: functions with the "riscv-interrupt-m" ABI cannot be `async`
--> $DIR/cannot-be-coroutine.rs:44:1
|
LL | async extern "riscv-interrupt-m" fn riscv_m() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `async` keyword from this definiton
|
LL - async extern "riscv-interrupt-m" fn riscv_m() {
LL + extern "riscv-interrupt-m" fn riscv_m() {
|
error: functions with the "riscv-interrupt-s" ABI cannot be `async`
--> $DIR/cannot-be-coroutine.rs:48:1
|
LL | async extern "riscv-interrupt-s" fn riscv_s() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the `async` keyword from this definiton
|
LL - async extern "riscv-interrupt-s" fn riscv_s() {
LL + extern "riscv-interrupt-s" fn riscv_s() {
|
error: requires `ResumeTy` lang_item
--> $DIR/cannot-be-coroutine.rs:32:19
|
LL | async fn vanilla(){
| ___________________^
LL | |
LL | | }
| |_^
error: aborting due to 3 previous errors

Some files were not shown because too many files have changed in this diff Show more