diff options
Diffstat (limited to 'compiler')
62 files changed, 1304 insertions, 170 deletions
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 7457ae1f033..1b8c2de1588 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -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", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 11afd359e5a..ab8dac16026 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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)] diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 6ac258155fe..dc571f5c367 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -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" } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f297bf9f4cf..c2140514e31 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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, diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index b8fa2dd3dd6..9b60807e650 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -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, diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index c738cb2aa2f..1940628b44a 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -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 diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 9a267501230..6eddce7b590 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index b69a91e2f5f..da248251203 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -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); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index c437e62f4d3..d387a4a310e 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index f6b5ff404db..7651e8365a2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -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, diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index ae3a1a41387..dc3598bcc36 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -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 } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 0fa69c40154..eadf8657a0f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -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 { diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs new file mode 100644 index 00000000000..f6c7ac5e3a3 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs @@ -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)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ac7e90fd902..d407669cb41 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -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; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 43b2d07dbe1..6ca5c64e0bc 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -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 ]; diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 32c71f433b0..7e356b4b462 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -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 diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index f769b393528..7bd27eb3ef1 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -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') { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e1d8b7546cf..db5ac6a514f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -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(..)) => { diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 887275e7294..f5b7a6066c8 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -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), + }) + } } } } diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs index 6b2ad30dffd..b4c4eac028f 100644 --- a/compiler/rustc_expand/src/stats.rs +++ b/compiler/rustc_expand/src/stats.rs @@ -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, - - /// 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 lines of code (when pretty-printed). + pub lines: usize, + + /// 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; } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3540c9b4bf0..8c0f279e343 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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: // ========================================================================== diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 91715851226..d9d5334615a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -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. diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index bd2252c1bf8..8c43d10b9e4 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -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}` diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 3bad36da999..b556683e80a 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -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; } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d3289e4cc6d..ff87d3dec6e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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); } diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 258535f3742..c21b16c9f9f 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -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 diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7a3647df0c4..f790c51f8f1 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -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 { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 5fea0c62843..3606c778fc4 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 672f3bc67ce..2bc9dadb665 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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!(), + } } } } diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index b06e0704b6f..80eab578f13 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -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 + } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index e8c23ef997c..edfb05e2ccd 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -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), ); } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index ac405277c4e..5bbe69c8d65 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -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 }, ); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 4d405d915fb..eedb897312e 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -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), ); } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index b9a014d14c0..d0e72a86d8a 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -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>, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index d8743814d79..c9ef723aea4 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -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]) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 13c281a6182..5cb943b0d8c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -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, } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 1534865cdd3..c4c2d8a7ac8 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -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" } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index fae159103e7..e339520cd86 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -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 diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index f8c64d7d13e..99148504a87 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -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 { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index b23bc089cd4..9e07dd5da7e 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -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 { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 34524aed406..5e4219dbf5b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -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 diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 2074fbce0ae..fe3d072fa88 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -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 { .. } => { diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs index 2dff26f02f3..675beceea14 100644 --- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs @@ -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) } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 977d4f3e931..270a7d4b154 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -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()) + } + } + } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 67988f1fcbc..1d15e7e126f 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -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>, @@ -175,6 +181,20 @@ struct BreakableScope<'tcx> { } #[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 region_scope: region::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. /// diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index d5061b71699..0b6b36640e9 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -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 { .. } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index ae09db50235..32df191cbca 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -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, +} diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 3baeccf6409..5197e93fda7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -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), diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 245bd866030..9fd410e6bf1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -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 { .. } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index db9547a481f..1507b6b8c06 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -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); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93c76c47f06..4e312aab497 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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()) diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 47e29923ee5..68d78af5943 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -301,6 +301,7 @@ fn emit_malformed_attribute( | sym::naked | sym::no_mangle | sym::must_use + | sym::track_caller ) { return; } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 29526817257..06acf7bf992 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -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 diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 25b84179443..99c220d946e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -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> { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 94c8ae77ed7..c933647165c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -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; diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index f7a4931c111..09685640e50 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -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 } diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index a4c6f186222..c0d9937e34d 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -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, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index b4239ddd896..2c652c7546e 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -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, diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 4415cd6e2e3..1ae79491642 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -1126,6 +1126,7 @@ pub enum Abi { RustCold, RiscvInterruptM, RiscvInterruptS, + RustInvalid, Custom, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5861ae4a372..c9262d24a17 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -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, diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index 42ec10a8e15..ce1bdcbb8ac 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -156,7 +156,8 @@ impl AbiMap { | ExternAbi::Msp430Interrupt | ExternAbi::RiscvInterruptM | ExternAbi::RiscvInterruptS - | ExternAbi::X86Interrupt, + | ExternAbi::X86Interrupt + | ExternAbi::RustInvalid, _, ) => return AbiMapping::Invalid, }; diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index b275cd382ab..60f8bd9d83a 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -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 { .. } |
