From 547f3cef6022980187da2744b3c6ed93368328bd Mon Sep 17 00:00:00 2001 From: Haydon Ryan Date: Mon, 11 Dec 2023 11:41:33 -0600 Subject: [PATCH 01/27] fixing error std::fs::read_to_string example documentation --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 4310e10830380..ed630ef3dc7f1 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -283,11 +283,11 @@ pub fn read>(path: P) -> io::Result> { /// /// ```no_run /// use std::fs; -/// use std::net::SocketAddr; /// use std::error::Error; /// /// fn main() -> Result<(), Box> { -/// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; +/// let address: String = fs::read_to_string("address.txt")?; +/// println!("{}", address); /// Ok(()) /// } /// ``` From 6e9ca489f703f055d98bf7a513b2491dbec63983 Mon Sep 17 00:00:00 2001 From: Haydon Ryan Date: Mon, 11 Dec 2023 12:00:23 -0600 Subject: [PATCH 02/27] Updating fs::read example to remove SocketAddress --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index ed630ef3dc7f1..acc0f37a688a8 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -242,10 +242,10 @@ pub struct DirBuilder { /// /// ```no_run /// use std::fs; -/// use std::net::SocketAddr; /// /// fn main() -> Result<(), Box> { -/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; +/// let address: String = String::from_utf8_lossy(&fs::read("address.txt")?).to_string(); +/// println!("{}", address); /// Ok(()) /// } /// ``` From f4b65f5caf7c7ea7a2df9cfe43727a3f9dc27f69 Mon Sep 17 00:00:00 2001 From: Kornel Date: Sat, 3 Feb 2024 11:01:26 +0000 Subject: [PATCH 03/27] Docs for std::ptr::slice_from_raw_parts --- library/core/src/ptr/mod.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index dce7e035fc73a..6e448d90c1224 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -762,6 +762,17 @@ pub const fn from_mut(r: &mut T) -> *mut T { /// let slice = ptr::slice_from_raw_parts(raw_pointer, 3); /// assert_eq!(unsafe { &*slice }[2], 7); /// ``` +/// +/// You must ensure that the pointer is valid and not null before dereferencing +/// the raw slice. A slice reference must never have a null pointer, even if it's empty. +/// +/// ```rust,should_panic +/// use std::ptr; +/// let danger: *const [u8] = ptr::slice_from_raw_parts(ptr::null(), 0); +/// unsafe { +/// danger.as_ref().expect("references must not be null"); +/// } +/// ``` #[inline] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[rustc_const_stable(feature = "const_slice_from_raw_parts", since = "1.64.0")] @@ -771,11 +782,13 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { from_raw_parts(data.cast(), len) } +/// Forms a raw mutable slice from a pointer and a length. +/// +/// The `len` argument is the number of **elements**, not the number of bytes. +/// /// Performs the same functionality as [`slice_from_raw_parts`], except that a /// raw mutable slice is returned, as opposed to a raw immutable slice. /// -/// See the documentation of [`slice_from_raw_parts`] for more details. -/// /// This function is safe, but actually using the return value is unsafe. /// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. /// @@ -796,6 +809,17 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { /// /// assert_eq!(unsafe { &*slice }[2], 99); /// ``` +/// +/// You must ensure that the pointer is valid and not null before dereferencing +/// the raw slice. A slice reference must never have a null pointer, even if it's empty. +/// +/// ```rust,should_panic +/// use std::ptr; +/// let danger: *mut [u8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 0); +/// unsafe { +/// danger.as_mut().expect("references must not be null"); +/// } +/// ``` #[inline] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] From 9539feb2f73b1e9370e28a6dcdaf0fe51bc65465 Mon Sep 17 00:00:00 2001 From: Haydon Ryan Date: Thu, 15 Feb 2024 08:20:15 -0600 Subject: [PATCH 04/27] Update library/std/src/fs.rs Co-authored-by: Mara Bos --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index acc0f37a688a8..29b61a8d1d0bc 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -286,8 +286,8 @@ pub fn read>(path: P) -> io::Result> { /// use std::error::Error; /// /// fn main() -> Result<(), Box> { -/// let address: String = fs::read_to_string("address.txt")?; -/// println!("{}", address); +/// let message: String = fs::read_to_string("message.txt")?; +/// println!("{}", message); /// Ok(()) /// } /// ``` From b5e1ca39180e9997efef14d3b7d06a3ddff72b59 Mon Sep 17 00:00:00 2001 From: Haydon Ryan Date: Thu, 15 Feb 2024 08:20:45 -0600 Subject: [PATCH 05/27] Update library/std/src/fs.rs Co-authored-by: Mara Bos --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 29b61a8d1d0bc..5bcd92b63df95 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -244,8 +244,8 @@ pub struct DirBuilder { /// use std::fs; /// /// fn main() -> Result<(), Box> { -/// let address: String = String::from_utf8_lossy(&fs::read("address.txt")?).to_string(); -/// println!("{}", address); +/// let data: Vec = fs::read("image.jpg")?; +/// assert_eq!(data[0..3], [0xFF, 0xD8, 0xFF]); /// Ok(()) /// } /// ``` From 93fa8579c6430d54525e1905eafff4dbcf9b31b0 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 Dec 2023 20:53:01 +0000 Subject: [PATCH 06/27] Add asm label support to AST and HIR --- compiler/rustc_ast/src/ast.rs | 5 +- compiler/rustc_ast/src/mut_visit.rs | 1 + compiler/rustc_ast/src/visit.rs | 1 + compiler/rustc_ast_lowering/messages.ftl | 3 + compiler/rustc_ast_lowering/src/asm.rs | 27 +++++++-- compiler/rustc_ast_lowering/src/errors.rs | 10 ++++ compiler/rustc_ast_pretty/src/pprust/state.rs | 4 ++ compiler/rustc_builtin_macros/src/asm.rs | 3 + compiler/rustc_codegen_ssa/src/mono_item.rs | 3 +- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/hir.rs | 14 ++++- compiler/rustc_hir/src/intravisit.rs | 1 + .../src/check/intrinsicck.rs | 2 + compiler/rustc_hir_pretty/src/lib.rs | 4 ++ compiler/rustc_hir_typeck/src/expr.rs | 3 + .../rustc_hir_typeck/src/expr_use_visitor.rs | 3 + compiler/rustc_mir_build/src/thir/cx/expr.rs | 3 + compiler/rustc_monomorphize/src/collector.rs | 3 +- compiler/rustc_parse/src/parser/stmt.rs | 2 +- compiler/rustc_passes/src/liveness.rs | 59 +++++++++++++++---- compiler/rustc_passes/src/naked_functions.rs | 3 +- compiler/rustc_resolve/src/late.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + .../clippy_lints/src/loops/never_loop.rs | 3 + .../clippy/clippy_utils/src/hir_utils.rs | 1 + 25 files changed, 142 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3fdb2a2225a58..d0287af9cd33c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2294,6 +2294,9 @@ pub enum InlineAsmOperand { Sym { sym: InlineAsmSym, }, + Label { + block: P, + }, } impl InlineAsmOperand { @@ -2303,7 +2306,7 @@ impl InlineAsmOperand { | Self::Out { reg, .. } | Self::InOut { reg, .. } | Self::SplitInOut { reg, .. } => Some(reg), - Self::Const { .. } | Self::Sym { .. } => None, + Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None, } } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index c42c41999732c..c2eddef9fb7f4 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1330,6 +1330,7 @@ pub fn noop_visit_inline_asm(asm: &mut InlineAsm, vis: &mut T) { } InlineAsmOperand::Const { anon_const } => vis.visit_anon_const(anon_const), InlineAsmOperand::Sym { sym } => vis.visit_inline_asm_sym(sym), + InlineAsmOperand::Label { block } => vis.visit_block(block), } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ecf379cc2408a..7fbfe4d97c059 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -885,6 +885,7 @@ pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) try_visit!(visitor.visit_anon_const(anon_const)) } InlineAsmOperand::Sym { sym } => try_visit!(visitor.visit_inline_asm_sym(sym)), + InlineAsmOperand::Label { block } => try_visit!(visitor.visit_block(block)), } } V::Result::output() diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index e87cf05713cd3..d91d65497e1c1 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -88,6 +88,9 @@ ast_lowering_invalid_abi_suggestion = did you mean ast_lowering_invalid_asm_template_modifier_const = asm template modifiers are not allowed for `const` arguments +ast_lowering_invalid_asm_template_modifier_label = + asm template modifiers are not allowed for `label` arguments + ast_lowering_invalid_asm_template_modifier_reg_class = invalid asm template modifier for this register class diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd717e82d26df..f91183e2cbf2e 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -3,9 +3,9 @@ use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringE use super::errors::{ AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported, InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst, - InvalidAsmTemplateModifierRegClass, InvalidAsmTemplateModifierRegClassSub, - InvalidAsmTemplateModifierSym, InvalidRegister, InvalidRegisterClass, RegisterClassOnlyClobber, - RegisterConflict, + InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass, + InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierSym, InvalidRegister, + InvalidRegisterClass, RegisterClassOnlyClobber, RegisterConflict, }; use super::LoweringContext; @@ -236,6 +236,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } + InlineAsmOperand::Label { block } => { + if !self.tcx.features().asm_goto { + feature_err( + sess, + sym::asm_goto, + *op_sp, + "label operands for inline assembly are unstable", + ) + .emit(); + } + hir::InlineAsmOperand::Label { block: self.lower_block(block, false) } + } }; (op, self.lower_span(*op_sp)) }) @@ -295,6 +307,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { op_span: op_sp, }); } + hir::InlineAsmOperand::Label { .. } => { + self.dcx().emit_err(InvalidAsmTemplateModifierLabel { + placeholder_span, + op_span: op_sp, + }); + } } } } @@ -334,7 +352,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => { + | hir::InlineAsmOperand::SymStatic { .. } + | hir::InlineAsmOperand::Label { .. } => { unreachable!("{op:?} is not a register operand"); } }; diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 834409da6750a..76659216d870e 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -262,6 +262,16 @@ pub struct InvalidAsmTemplateModifierSym { pub op_span: Span, } +#[derive(Diagnostic, Clone, Copy)] +#[diag(ast_lowering_invalid_asm_template_modifier_label)] +pub struct InvalidAsmTemplateModifierLabel { + #[primary_span] + #[label(ast_lowering_template_modifier)] + pub placeholder_span: Span, + #[label(ast_lowering_argument)] + pub op_span: Span, +} + #[derive(Diagnostic, Clone, Copy)] #[diag(ast_lowering_register_class_only_clobber)] pub struct RegisterClassOnlyClobber { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 7ea0078ea3bb9..8d64ecb24309a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1331,6 +1331,10 @@ impl<'a> State<'a> { s.print_path(&sym.path, true, 0); } } + InlineAsmOperand::Label { block } => { + s.head("label"); + s.print_block(block); + } } } AsmArg::ClobberAbi(abi) => { diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index c5a73c3199579..93eb3a9a43ee3 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -166,6 +166,9 @@ pub fn parse_asm_args<'a>( path: path.clone(), }; ast::InlineAsmOperand::Sym { sym } + } else if !is_global_asm && p.eat_keyword(sym::label) { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } } else if allow_templates { let template = p.parse_expr()?; // If it can't possibly expand to a string, provide diagnostics here to include other diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 295e27691090c..1a4795c0213a1 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -76,7 +76,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::InOut { .. } - | hir::InlineAsmOperand::SplitInOut { .. } => { + | hir::InlineAsmOperand::SplitInOut { .. } + | hir::InlineAsmOperand::Label { .. } => { span_bug!(*op_sp, "invalid operand type for global_asm!") } }) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 93c183a65ef3e..74ec6a9713f3c 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -345,6 +345,8 @@ declare_features! ( (unstable, asm_const, "1.58.0", Some(93332)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), + /// Allows using `label` operands in inline assembly. + (unstable, asm_goto, "CURRENT_RUSTC_VERSION", Some(119364)), /// Allows the `may_unwind` option in inline assembly. (unstable, asm_unwind, "1.58.0", Some(93334)), /// Allows users to enforce equality of associated constants `TraitImpl`. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 78e7c636a3e74..2b8cd47bd1fe0 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2645,6 +2645,9 @@ pub enum InlineAsmOperand<'hir> { path: QPath<'hir>, def_id: DefId, }, + Label { + block: &'hir Block<'hir>, + }, } impl<'hir> InlineAsmOperand<'hir> { @@ -2654,7 +2657,10 @@ impl<'hir> InlineAsmOperand<'hir> { | Self::Out { reg, .. } | Self::InOut { reg, .. } | Self::SplitInOut { reg, .. } => Some(reg), - Self::Const { .. } | Self::SymFn { .. } | Self::SymStatic { .. } => None, + Self::Const { .. } + | Self::SymFn { .. } + | Self::SymStatic { .. } + | Self::Label { .. } => None, } } @@ -2675,6 +2681,12 @@ pub struct InlineAsm<'hir> { pub line_spans: &'hir [Span], } +impl InlineAsm<'_> { + pub fn contains_label(&self) -> bool { + self.operands.iter().any(|x| matches!(x.0, InlineAsmOperand::Label { .. })) + } +} + /// Represents a parameter in a function header. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Param<'hir> { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 1c38a45d3a32f..278a0a6e6b906 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1290,6 +1290,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>( InlineAsmOperand::SymStatic { path, .. } => { try_visit!(visitor.visit_qpath(path, id, *op_sp)); } + InlineAsmOperand::Label { block } => try_visit!(visitor.visit_block(block)), } } V::Result::output() diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index d03b02f028da1..9de660407d7aa 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -470,6 +470,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } }; } + // No special checking is needed for labels. + hir::InlineAsmOperand::Label { .. } => {} } } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 8f8f747339b42..b5bb063c5ed8c 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1265,6 +1265,10 @@ impl<'a> State<'a> { s.space(); s.print_qpath(path, true); } + hir::InlineAsmOperand::Label { block } => { + s.head("label"); + s.print_block(block); + } }, AsmArg::Options(opts) => { s.word("options"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 81440b0562e24..614761c03bd61 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3264,6 +3264,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // be well-formed. hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} hir::InlineAsmOperand::SymStatic { .. } => {} + hir::InlineAsmOperand::Label { block } => { + self.check_block_no_value(block); + } } } if asm.options.contains(ast::InlineAsmOptions::NORETURN) { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 04fb7bcf4f3b4..ba0383d19b92a 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -293,6 +293,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { | hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymStatic { .. } => {} + hir::InlineAsmOperand::Label { block } => { + self.walk_block(block); + } } } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 62762168cf420..0e64ed897537e 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -656,6 +656,9 @@ impl<'tcx> Cx<'tcx> { hir::InlineAsmOperand::SymStatic { path: _, def_id } => { InlineAsmOperand::SymStatic { def_id } } + hir::InlineAsmOperand::Label { .. } => { + todo!() + } }) .collect(), options: asm.options, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5593de607844d..3ab6c4b9b6a55 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -446,7 +446,8 @@ fn collect_items_rec<'tcx>( hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::InOut { .. } - | hir::InlineAsmOperand::SplitInOut { .. } => { + | hir::InlineAsmOperand::SplitInOut { .. } + | hir::InlineAsmOperand::Label { .. } => { span_bug!(*op_sp, "invalid operand type for global_asm!") } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 1bae5b3224035..9c0020f02d9c2 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -434,7 +434,7 @@ impl<'a> Parser<'a> { } /// Parses a block. No inner attributes are allowed. - pub(super) fn parse_block(&mut self) -> PResult<'a, P> { + pub fn parse_block(&mut self) -> PResult<'a, P> { let (attrs, block) = self.parse_inner_attrs_and_block()?; if let [.., last] = &*attrs { self.error_on_forbidden_inner_attr( diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 487407014d178..e5033e1f51f23 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -86,7 +86,6 @@ use crate::errors; use self::LiveNodeKind::*; use self::VarKind::*; -use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::*; @@ -416,6 +415,12 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); } + // Inline assembly may contain labels. + hir::ExprKind::InlineAsm(asm) if asm.contains_label() => { + self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); + intravisit::walk_expr(self, expr); + } + // otherwise, live nodes are not required: hir::ExprKind::Index(..) | hir::ExprKind::Field(..) @@ -1045,20 +1050,53 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { | hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ), hir::ExprKind::InlineAsm(asm) => { + // + // (inputs) + // | + // v + // (outputs) + // / \ + // | | + // v v + // (labels)(fallthrough) + // | | + // v v + // ( succ / exit_ln ) + // Handle non-returning asm - let mut succ = if asm.options.contains(InlineAsmOptions::NORETURN) { - self.exit_ln - } else { - succ - }; + let mut succ = + if self.typeck_results.expr_ty(expr).is_never() { self.exit_ln } else { succ }; + + // Do a first pass for labels only + if asm.contains_label() { + let ln = self.live_node(expr.hir_id, expr.span); + self.init_from_succ(ln, succ); + for (op, _op_sp) in asm.operands.iter().rev() { + match op { + hir::InlineAsmOperand::Label { block } => { + let label_ln = self.propagate_through_block(block, succ); + self.merge_from_succ(ln, label_ln); + } + hir::InlineAsmOperand::In { .. } + | hir::InlineAsmOperand::Out { .. } + | hir::InlineAsmOperand::InOut { .. } + | hir::InlineAsmOperand::SplitInOut { .. } + | hir::InlineAsmOperand::Const { .. } + | hir::InlineAsmOperand::SymFn { .. } + | hir::InlineAsmOperand::SymStatic { .. } => {} + } + } + succ = ln; + } - // Do a first pass for writing outputs only + // Do a second pass for writing outputs only for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => {} + | hir::InlineAsmOperand::SymStatic { .. } + | hir::InlineAsmOperand::Label { .. } => {} hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { succ = self.write_place(expr, succ, ACC_WRITE); @@ -1075,7 +1113,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } } - // Then do a second pass for inputs + // Then do a third pass for inputs for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { expr, .. } => { @@ -1097,7 +1135,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => {} + | hir::InlineAsmOperand::SymStatic { .. } + | hir::InlineAsmOperand::Label { .. } => {} } } succ diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 0455d6d4acba3..27c9c1306e6ac 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -237,7 +237,8 @@ impl<'tcx> CheckInlineAssembly<'tcx> { InlineAsmOperand::In { .. } | InlineAsmOperand::Out { .. } | InlineAsmOperand::InOut { .. } - | InlineAsmOperand::SplitInOut { .. } => Some(op_sp), + | InlineAsmOperand::SplitInOut { .. } + | InlineAsmOperand::Label { .. } => Some(op_sp), }) .collect(); if !unsupported_operands.is_empty() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2f4da29133f1b..8b8d99f88165a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1246,6 +1246,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.resolve_anon_const(anon_const, AnonConstKind::InlineConst); } InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym), + InlineAsmOperand::Label { block } => self.visit_block(block), } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 46472a131ff4b..0cfff1623dbd4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -399,6 +399,7 @@ symbols! { asm, asm_const, asm_experimental_arch, + asm_goto, asm_sym, asm_unwind, assert, diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 245a903f99826..65d922f03df3a 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -255,6 +255,9 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, + InlineAsmOperand::Label { block } => { + never_loop_block(cx, block, local_labels, main_loop_id) + } })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index d50332e82da5b..643852c1c54fb 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -835,6 +835,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_body(anon_const.body); }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), + InlineAsmOperand::Label { block } => self.hash_block(block), } } }, From 040ab7d4b604d9e70bf9c93a1af7146edf0e740e Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 Dec 2023 21:19:04 +0000 Subject: [PATCH 07/27] Add asm label support to THIR --- compiler/rustc_middle/src/thir.rs | 3 +++ compiler/rustc_middle/src/thir/visit.rs | 1 + compiler/rustc_mir_build/src/build/expr/into.rs | 3 +++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 4 ++-- compiler/rustc_mir_build/src/thir/print.rs | 6 ++++++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e1e5d68148fe8..f91130ee25820 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -565,6 +565,9 @@ pub enum InlineAsmOperand<'tcx> { SymStatic { def_id: DefId, }, + Label { + block: BlockId, + }, } #[derive(Copy, Clone, Debug, PartialEq, HashStable)] diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 4847a7bea91d7..5952c296fb642 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -162,6 +162,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( | Const { value: _, span: _ } | SymFn { value: _, span: _ } | SymStatic { def_id: _ } => {} + Label { block } => visitor.visit_block(&visitor.thir()[*block]), } } } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 2978491d646e8..60614383cc84a 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -455,6 +455,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { thir::InlineAsmOperand::SymStatic { def_id } => { mir::InlineAsmOperand::SymStatic { def_id } } + thir::InlineAsmOperand::Label { .. } => { + todo!() + } }) .collect(); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 0e64ed897537e..2318e84292b83 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -656,8 +656,8 @@ impl<'tcx> Cx<'tcx> { hir::InlineAsmOperand::SymStatic { path: _, def_id } => { InlineAsmOperand::SymStatic { def_id } } - hir::InlineAsmOperand::Label { .. } => { - todo!() + hir::InlineAsmOperand::Label { block } => { + InlineAsmOperand::Label { block: self.mirror_block(block) } } }) .collect(), diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 267ea3aa3e12c..d53704f89e79e 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -889,6 +889,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1); print_indented!(self, "}", depth_lvl + 1); } + InlineAsmOperand::Label { block } => { + print_indented!(self, "InlineAsmOperand::Block {", depth_lvl); + print_indented!(self, "block:", depth_lvl + 1); + self.print_block(*block, depth_lvl + 2); + print_indented!(self, "}", depth_lvl + 1); + } } } } From 7152993aa859819d67b9f01e41e1f389e52043de Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 26 Dec 2023 04:31:56 +0000 Subject: [PATCH 08/27] Use slice.chain(option) for Successors This makes more sense because most cases then second one is unwind target. --- compiler/rustc_middle/src/mir/terminator.rs | 59 ++++++++++----------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 91b7952bec5e8..381db830aea5e 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -336,8 +336,7 @@ pub struct Terminator<'tcx> { } pub type Successors<'a> = impl DoubleEndedIterator + 'a; -pub type SuccessorsMut<'a> = - iter::Chain, slice::IterMut<'a, BasicBlock>>; +pub type SuccessorsMut<'a> = impl DoubleEndedIterator + 'a; impl<'tcx> Terminator<'tcx> { #[inline] @@ -371,24 +370,24 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn successors(&self) -> Successors<'_> { use self::TerminatorKind::*; match *self { - Call { target: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } - | Yield { resume: t, drop: Some(ref u), .. } - | Drop { target: t, unwind: UnwindAction::Cleanup(ref u), .. } - | Assert { target: t, unwind: UnwindAction::Cleanup(ref u), .. } - | FalseUnwind { real_target: t, unwind: UnwindAction::Cleanup(ref u) } - | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } => { - Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied()) - } - Goto { target: t } - | Call { target: None, unwind: UnwindAction::Cleanup(t), .. } - | Call { target: Some(t), unwind: _, .. } - | Yield { resume: t, drop: None, .. } - | Drop { target: t, unwind: _, .. } - | Assert { target: t, unwind: _, .. } - | FalseUnwind { real_target: t, unwind: _ } - | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } - | InlineAsm { destination: Some(t), unwind: _, .. } => { - Some(t).into_iter().chain((&[]).into_iter().copied()) + Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } + | Yield { resume: ref t, drop: Some(u), .. } + | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. } + | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. } + | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } + | InlineAsm { destination: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } => { + slice::from_ref(t).into_iter().copied().chain(Some(u)) + } + Goto { target: ref t } + | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. } + | Call { target: Some(ref t), unwind: _, .. } + | Yield { resume: ref t, drop: None, .. } + | Drop { target: ref t, unwind: _, .. } + | Assert { target: ref t, unwind: _, .. } + | FalseUnwind { real_target: ref t, unwind: _ } + | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(ref t), .. } + | InlineAsm { destination: Some(ref t), unwind: _, .. } => { + slice::from_ref(t).into_iter().copied().chain(None) } UnwindResume | UnwindTerminate(_) @@ -397,14 +396,12 @@ impl<'tcx> TerminatorKind<'tcx> { | Unreachable | Call { target: None, unwind: _, .. } | InlineAsm { destination: None, unwind: _, .. } => { - None.into_iter().chain((&[]).into_iter().copied()) + (&[]).into_iter().copied().chain(None) } - SwitchInt { ref targets, .. } => { - None.into_iter().chain(targets.targets.iter().copied()) + SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None), + FalseEdge { ref real_target, imaginary_target } => { + slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target)) } - FalseEdge { real_target, ref imaginary_target } => Some(real_target) - .into_iter() - .chain(slice::from_ref(imaginary_target).into_iter().copied()), } } @@ -421,7 +418,7 @@ impl<'tcx> TerminatorKind<'tcx> { destination: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. - } => Some(t).into_iter().chain(slice::from_mut(u)), + } => slice::from_mut(t).into_iter().chain(Some(u)), Goto { target: ref mut t } | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } | Call { target: Some(ref mut t), unwind: _, .. } @@ -431,7 +428,7 @@ impl<'tcx> TerminatorKind<'tcx> { | FalseUnwind { real_target: ref mut t, unwind: _ } | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(ref mut t), .. } | InlineAsm { destination: Some(ref mut t), unwind: _, .. } => { - Some(t).into_iter().chain(&mut []) + slice::from_mut(t).into_iter().chain(None) } UnwindResume | UnwindTerminate(_) @@ -439,10 +436,10 @@ impl<'tcx> TerminatorKind<'tcx> { | Return | Unreachable | Call { target: None, unwind: _, .. } - | InlineAsm { destination: None, unwind: _, .. } => None.into_iter().chain(&mut []), - SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets), + | InlineAsm { destination: None, unwind: _, .. } => (&mut []).into_iter().chain(None), + SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None), FalseEdge { ref mut real_target, ref mut imaginary_target } => { - Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) + slice::from_mut(real_target).into_iter().chain(Some(imaginary_target)) } } } From b044aaa905f8a97e67ea72b490ee46d6420c4680 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 26 Dec 2023 15:28:42 +0000 Subject: [PATCH 09/27] Change InlineAsm to allow multiple targets instead --- compiler/rustc_borrowck/src/lib.rs | 2 +- .../src/polonius/loan_invalidations.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 4 +- compiler/rustc_codegen_cranelift/src/base.rs | 16 ++++- compiler/rustc_codegen_ssa/src/mir/block.rs | 4 +- compiler/rustc_const_eval/messages.ftl | 3 - .../rustc_const_eval/src/interpret/machine.rs | 6 ++ .../src/interpret/terminator.rs | 12 +--- .../src/transform/validate.rs | 6 +- compiler/rustc_middle/src/mir/mod.rs | 6 +- compiler/rustc_middle/src/mir/pretty.rs | 23 ++++--- compiler/rustc_middle/src/mir/syntax.rs | 7 +- compiler/rustc_middle/src/mir/terminator.rs | 64 ++++++++++--------- compiler/rustc_middle/src/mir/visit.rs | 2 +- .../rustc_mir_build/src/build/expr/into.rs | 6 +- compiler/rustc_mir_build/src/lints.rs | 7 +- .../src/framework/direction.rs | 13 ++-- .../src/framework/graphviz.rs | 4 +- .../src/move_paths/builder.rs | 2 +- .../rustc_mir_transform/src/coverage/graph.rs | 18 ++++-- .../rustc_mir_transform/src/coverage/tests.rs | 5 +- compiler/rustc_mir_transform/src/inline.rs | 4 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 5 +- 23 files changed, 125 insertions(+), 96 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index dbaa9e5bcfab0..94c1875608387 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -723,7 +723,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro operands, options: _, line_spans: _, - destination: _, + targets: _, unwind: _, } => { for op in operands { diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 10941cadcbb25..83081f83e5e80 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -161,7 +161,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { operands, options: _, line_spans: _, - destination: _, + targets: _, unwind: _, } => { for op in operands { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 75cc28bcab0bc..fecf23ad74a8d 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1771,8 +1771,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, real_target, is_cleanup); self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); } - TerminatorKind::InlineAsm { destination, unwind, .. } => { - if let Some(target) = destination { + TerminatorKind::InlineAsm { ref targets, unwind, .. } => { + for &target in targets { self.assert_iscleanup(body, block_data, target, is_cleanup); } self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index a7e76fbc128ea..1ce920f3bdb79 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -445,7 +445,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { template, operands, options, - destination, + targets, line_spans: _, unwind: _, } => { @@ -456,13 +456,25 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } + let have_labels = if options.contains(InlineAsmOptions::NORETURN) { + !targets.is_empty() + } else { + targets.len() > 1 + }; + if have_labels { + fx.tcx.dcx().span_fatal( + source_info.span, + "cranelift doesn't support labels in inline assembly.", + ); + } + crate::inline_asm::codegen_inline_asm_terminator( fx, source_info.span, template, operands, *options, - *destination, + targets.get(0).copied(), ); } TerminatorKind::UnwindTerminate(reason) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 00007110938e0..6342802bb1960 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1285,7 +1285,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ref operands, options, line_spans, - destination, + ref targets, unwind, } => self.codegen_asm_terminator( helper, @@ -1295,7 +1295,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operands, options, line_spans, - destination, + targets.get(0).copied(), unwind, self.instance, mergeable_succ(), diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index c456e40d7c15b..52ee78570bef5 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -232,9 +232,6 @@ const_eval_non_const_fn_call = const_eval_non_const_impl = impl defined here, but it is not `const` -const_eval_noreturn_asm_returned = - returned from noreturn inline assembly - const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 0106ec425bc50..1d858dd4f5665 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -374,11 +374,17 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { kind: Option>, ) -> InterpResult<'tcx, Cow<'b, Allocation>>; + /// Evaluate the inline assembly. + /// + /// This should take care of jumping to the next block (one of `targets`) when asm goto + /// is triggered, `targets[0]` when the assembly falls through, or diverge in case of + /// `InlineAsmOptions::NORETURN` being set. fn eval_inline_asm( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _template: &'tcx [InlineAsmTemplatePiece], _operands: &[mir::InlineAsmOperand<'tcx>], _options: InlineAsmOptions, + _targets: &[mir::BasicBlock], ) -> InterpResult<'tcx> { throw_unsup_format!("inline assembly is not supported") } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index e72ace8be3559..9b16d99d82b38 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; -use rustc_ast::ast::InlineAsmOptions; use rustc_middle::{ mir, ty::{ @@ -224,15 +223,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { terminator.kind ), - InlineAsm { template, ref operands, options, destination, .. } => { - M::eval_inline_asm(self, template, operands, options)?; - if options.contains(InlineAsmOptions::NORETURN) { - throw_ub_custom!(fluent::const_eval_noreturn_asm_returned); - } - self.go_to_block( - destination - .expect("InlineAsm terminators without noreturn must have a destination"), - ) + InlineAsm { template, ref operands, options, ref targets, .. } => { + M::eval_inline_asm(self, template, operands, options, targets)?; } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cc49e8ea247f7..74ba2f6039e86 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -471,9 +471,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *real_target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); } - TerminatorKind::InlineAsm { destination, unwind, .. } => { - if let Some(destination) = destination { - self.check_edge(location, *destination, EdgeKind::Normal); + TerminatorKind::InlineAsm { targets, unwind, .. } => { + for &target in targets { + self.check_edge(location, target, EdgeKind::Normal); } self.check_unwind_edge(location, *unwind); } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 3017f912ef027..de18580f61ae9 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1718,13 +1718,13 @@ mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; // tidy-alphabetical-start - static_assert_size!(BasicBlockData<'_>, 136); + static_assert_size!(BasicBlockData<'_>, 144); static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(SourceScopeData<'_>, 72); static_assert_size!(Statement<'_>, 32); static_assert_size!(StatementKind<'_>, 16); - static_assert_size!(Terminator<'_>, 104); - static_assert_size!(TerminatorKind<'_>, 88); + static_assert_size!(Terminator<'_>, 112); + static_assert_size!(TerminatorKind<'_>, 96); static_assert_size!(VarDebugInfo<'_>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 5638b575b319c..35058458eed7c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use crate::mir::interpret::ConstAllocation; use super::graphviz::write_mir_fn_graphviz; -use rustc_ast::InlineAsmTemplatePiece; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::mir::interpret::{ alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer, Provenance, @@ -868,16 +868,19 @@ impl<'tcx> TerminatorKind<'tcx> { vec!["real".into(), "unwind".into()] } FalseUnwind { unwind: _, .. } => vec!["real".into()], - InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => { - vec!["return".into(), "unwind".into()] - } - InlineAsm { destination: Some(_), unwind: _, .. } => { - vec!["return".into()] - } - InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => { - vec!["unwind".into()] + InlineAsm { options, ref targets, unwind, .. } => { + let mut vec = Vec::with_capacity(targets.len() + 1); + if !options.contains(InlineAsmOptions::NORETURN) { + vec.push("return".into()); + } + vec.resize(targets.len(), "label".into()); + + if let UnwindAction::Cleanup(_) = unwind { + vec.push("unwind".into()); + } + + vec } - InlineAsm { destination: None, unwind: _, .. } => vec![], } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 5c9857b9c53d8..6f62c7186a768 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -793,9 +793,10 @@ pub enum TerminatorKind<'tcx> { /// used to map assembler errors back to the line in the source code. line_spans: &'tcx [Span], - /// Destination block after the inline assembly returns, unless it is - /// diverging (InlineAsmOptions::NORETURN). - destination: Option, + /// Valid targets for the inline assembly. + /// The first element is the fallthrough destination, unless + /// InlineAsmOptions::NORETURN is set. + targets: Vec, /// Action to be taken if the inline assembly unwinds. This is present /// if and only if InlineAsmOptions::MAY_UNWIND is set. diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 381db830aea5e..fa4a494ff0b64 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -374,8 +374,7 @@ impl<'tcx> TerminatorKind<'tcx> { | Yield { resume: ref t, drop: Some(u), .. } | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. } | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. } - | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } - | InlineAsm { destination: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } => { + | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => { slice::from_ref(t).into_iter().copied().chain(Some(u)) } Goto { target: ref t } @@ -384,9 +383,7 @@ impl<'tcx> TerminatorKind<'tcx> { | Yield { resume: ref t, drop: None, .. } | Drop { target: ref t, unwind: _, .. } | Assert { target: ref t, unwind: _, .. } - | FalseUnwind { real_target: ref t, unwind: _ } - | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(ref t), .. } - | InlineAsm { destination: Some(ref t), unwind: _, .. } => { + | FalseUnwind { real_target: ref t, unwind: _ } => { slice::from_ref(t).into_iter().copied().chain(None) } UnwindResume @@ -394,10 +391,11 @@ impl<'tcx> TerminatorKind<'tcx> { | CoroutineDrop | Return | Unreachable - | Call { target: None, unwind: _, .. } - | InlineAsm { destination: None, unwind: _, .. } => { - (&[]).into_iter().copied().chain(None) + | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), + InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { + targets.iter().copied().chain(Some(u)) } + InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None), SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None), FalseEdge { ref real_target, imaginary_target } => { slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target)) @@ -413,21 +411,16 @@ impl<'tcx> TerminatorKind<'tcx> { | Yield { resume: ref mut t, drop: Some(ref mut u), .. } | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } - | FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) } - | InlineAsm { - destination: Some(ref mut t), - unwind: UnwindAction::Cleanup(ref mut u), - .. - } => slice::from_mut(t).into_iter().chain(Some(u)), + | FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) } => { + slice::from_mut(t).into_iter().chain(Some(u)) + } Goto { target: ref mut t } | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } | Call { target: Some(ref mut t), unwind: _, .. } | Yield { resume: ref mut t, drop: None, .. } | Drop { target: ref mut t, unwind: _, .. } | Assert { target: ref mut t, unwind: _, .. } - | FalseUnwind { real_target: ref mut t, unwind: _ } - | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(ref mut t), .. } - | InlineAsm { destination: Some(ref mut t), unwind: _, .. } => { + | FalseUnwind { real_target: ref mut t, unwind: _ } => { slice::from_mut(t).into_iter().chain(None) } UnwindResume @@ -435,8 +428,11 @@ impl<'tcx> TerminatorKind<'tcx> { | CoroutineDrop | Return | Unreachable - | Call { target: None, unwind: _, .. } - | InlineAsm { destination: None, unwind: _, .. } => (&mut []).into_iter().chain(None), + | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), + InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { + targets.iter_mut().chain(Some(u)) + } + InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None), SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None), FalseEdge { ref mut real_target, ref mut imaginary_target } => { slice::from_mut(real_target).into_iter().chain(Some(imaginary_target)) @@ -511,7 +507,7 @@ pub enum TerminatorEdges<'mir, 'tcx> { Double(BasicBlock, BasicBlock), /// Special action for `Yield`, `Call` and `InlineAsm` terminators. AssignOnReturn { - return_: Option, + return_: &'mir [BasicBlock], /// The cleanup block, if it exists. cleanup: Option, place: CallReturnPlaces<'mir, 'tcx>, @@ -575,31 +571,37 @@ impl<'tcx> TerminatorKind<'tcx> { TerminatorEdges::Double(real_target, imaginary_target) } - Yield { resume: target, drop, resume_arg, value: _ } => { + Yield { resume: ref target, drop, resume_arg, value: _ } => { TerminatorEdges::AssignOnReturn { - return_: Some(target), + return_: slice::from_ref(target), cleanup: drop, place: CallReturnPlaces::Yield(resume_arg), } } - Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => { - TerminatorEdges::AssignOnReturn { - return_: target, - cleanup: unwind.cleanup_block(), - place: CallReturnPlaces::Call(destination), - } - } + Call { + unwind, + destination, + ref target, + func: _, + args: _, + fn_span: _, + call_source: _, + } => TerminatorEdges::AssignOnReturn { + return_: target.as_ref().map(slice::from_ref).unwrap_or_default(), + cleanup: unwind.cleanup_block(), + place: CallReturnPlaces::Call(destination), + }, InlineAsm { template: _, ref operands, options: _, line_spans: _, - destination, + ref targets, unwind, } => TerminatorEdges::AssignOnReturn { - return_: destination, + return_: targets, cleanup: unwind.cleanup_block(), place: CallReturnPlaces::InlineAsm(operands), }, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 2c5ca82a4cd39..a1418b41e057f 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -565,7 +565,7 @@ macro_rules! make_mir_visitor { operands, options: _, line_spans: _, - destination: _, + targets: _, unwind: _, } => { for op in operands { diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 60614383cc84a..a49d864701a03 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -474,10 +474,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { operands, options, line_spans, - destination: if options.contains(InlineAsmOptions::NORETURN) { - None + targets: if options.contains(InlineAsmOptions::NORETURN) { + Vec::new() } else { - Some(destination_block) + vec![destination_block] }, unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) { UnwindAction::Continue diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 3f2b7c482a67f..2c817d605af2a 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -199,9 +199,10 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor ControlFlow::Break(NonRecursive), - // A diverging InlineAsm is treated as non-recursing - TerminatorKind::InlineAsm { destination, .. } => { - if destination.is_some() { + // A InlineAsm without targets (diverging and contains no labels) + // is treated as non-recursing. + TerminatorKind::InlineAsm { ref targets, .. } => { + if !targets.is_empty() { ControlFlow::Continue(()) } else { ControlFlow::Break(NonRecursive) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 4c3fadf487b3c..f57e8b8bd6f97 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -242,9 +242,9 @@ impl Direction for Backward { propagate(pred, &tmp); } - mir::TerminatorKind::InlineAsm { - destination: Some(dest), ref operands, .. - } if dest == bb => { + mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. } + if targets.contains(&bb) => + { let mut tmp = exit_state.clone(); analysis.apply_call_return_effect( &mut tmp, @@ -491,9 +491,12 @@ impl Direction for Forward { if let Some(cleanup) = cleanup { propagate(cleanup, exit_state); } - if let Some(return_) = return_ { + + if !return_.is_empty() { analysis.apply_call_return_effect(exit_state, bb, place); - propagate(return_, exit_state); + for &target in return_ { + propagate(target, exit_state); + } } } TerminatorEdges::SwitchInt { targets, discr } => { diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 0270e059a5821..a827f6a8dbd9d 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -299,7 +299,9 @@ where })?; } - mir::TerminatorKind::InlineAsm { destination: Some(_), ref operands, .. } => { + mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. } + if !targets.is_empty() => + { self.write_row(w, "", "(on successful return)", |this, w, fmt| { let state_on_unwind = this.results.get().clone(); this.results.apply_custom_effect(|analysis, state| { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index c6ec1b5aee454..95f12301395c3 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -491,7 +491,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { ref operands, options: _, line_spans: _, - destination: _, + targets: _, unwind: _, } => { for op in operands { diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index c97192435ce48..ed8c4d8283d98 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -349,12 +349,20 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera | FalseUnwind { real_target: target, .. } | Goto { target } => CoverageSuccessors::Chainable(target), - // These terminators can normally be chained, except when they have no + // A call terminator can normally be chained, except when they have no // successor because they are known to diverge. - Call { target: maybe_target, .. } | InlineAsm { destination: maybe_target, .. } => { - match maybe_target { - Some(target) => CoverageSuccessors::Chainable(target), - None => CoverageSuccessors::NotChainable(&[]), + Call { target: maybe_target, .. } => match maybe_target { + Some(target) => CoverageSuccessors::Chainable(target), + None => CoverageSuccessors::NotChainable(&[]), + }, + + // An inline asm terminator can normally be chained, except when it diverges or uses asm + // goto. + InlineAsm { ref targets, .. } => { + if targets.len() == 1 { + CoverageSuccessors::Chainable(targets[0]) + } else { + CoverageSuccessors::NotChainable(targets) } } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index d9a3c0cb162f3..569998de35e0b 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -88,7 +88,6 @@ impl<'tcx> MockBlocks<'tcx> { | TerminatorKind::FalseEdge { real_target: ref mut target, .. } | TerminatorKind::FalseUnwind { real_target: ref mut target, .. } | TerminatorKind::Goto { ref mut target } - | TerminatorKind::InlineAsm { destination: Some(ref mut target), .. } | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block, ref invalid => bug!("Invalid from_block: {:?}", invalid), } @@ -185,10 +184,12 @@ fn debug_basic_blocks(mir_body: &Body<'_>) -> String { | TerminatorKind::FalseEdge { real_target: target, .. } | TerminatorKind::FalseUnwind { real_target: target, .. } | TerminatorKind::Goto { target } - | TerminatorKind::InlineAsm { destination: Some(target), .. } | TerminatorKind::Yield { resume: target, .. } => { format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), target) } + TerminatorKind::InlineAsm { targets, .. } => { + format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), targets) + } TerminatorKind::SwitchInt { targets, .. } => { format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), targets) } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 2009539d4d084..a6382584577ce 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1032,8 +1032,8 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { { bug!("False unwinds should have been removed before inlining") } - TerminatorKind::InlineAsm { ref mut destination, ref mut unwind, .. } => { - if let Some(ref mut tgt) = *destination { + TerminatorKind::InlineAsm { ref mut targets, ref mut unwind, .. } => { + for tgt in targets.iter_mut() { *tgt = self.map_block(*tgt); } *unwind = self.map_unwind(*unwind); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 501d6f7d304ef..92e86cf0ee41e 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -632,14 +632,15 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { operands, options, line_spans, - destination, + targets, unwind, } => TerminatorKind::InlineAsm { template: format!("{template:?}"), operands: operands.iter().map(|operand| operand.stable(tables)).collect(), options: format!("{options:?}"), line_spans: format!("{line_spans:?}"), - destination: destination.map(|d| d.as_usize()), + // FIXME: Figure out how to do labels in SMIR + destination: targets.first().map(|d| d.as_usize()), unwind: unwind.stable(tables), }, mir::TerminatorKind::Yield { .. } From 3b1dd1bfa97aa9775109266f9932027a99362b0d Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 26 Dec 2023 16:07:35 +0000 Subject: [PATCH 10/27] Implement asm goto in MIR and MIR lowering --- compiler/rustc_borrowck/src/lib.rs | 3 +- .../src/polonius/loan_invalidations.rs | 3 +- .../rustc_codegen_cranelift/src/global_asm.rs | 3 +- .../rustc_codegen_cranelift/src/inline_asm.rs | 3 ++ compiler/rustc_codegen_ssa/src/mir/block.rs | 3 ++ compiler/rustc_middle/src/mir/pretty.rs | 3 ++ compiler/rustc_middle/src/mir/syntax.rs | 4 +++ compiler/rustc_middle/src/mir/visit.rs | 3 +- .../rustc_mir_build/src/build/expr/into.rs | 33 ++++++++++++++----- .../src/impls/storage_liveness.rs | 3 +- .../src/move_paths/builder.rs | 3 +- compiler/rustc_mir_transform/src/dest_prop.rs | 3 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 3 +- 13 files changed, 53 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 94c1875608387..b776643763a7e 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -749,7 +749,8 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } InlineAsmOperand::Const { value: _ } | InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} + | InlineAsmOperand::SymStatic { def_id: _ } + | InlineAsmOperand::Label { target_index: _ } => {} } } } diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 83081f83e5e80..956de1dec9b2b 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -182,7 +182,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { } InlineAsmOperand::Const { value: _ } | InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} + | InlineAsmOperand::SymStatic { def_id: _ } + | InlineAsmOperand::Label { target_index: _ } => {} } } } diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index da07b66c762ee..44650898de897 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -78,7 +78,8 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, InlineAsmOperand::In { .. } | InlineAsmOperand::Out { .. } | InlineAsmOperand::InOut { .. } - | InlineAsmOperand::SplitInOut { .. } => { + | InlineAsmOperand::SplitInOut { .. } + | InlineAsmOperand::Label { .. } => { span_bug!(op_sp, "invalid operand type for global_asm!") } } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 7793b1b70924b..171ee88a11c75 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -129,6 +129,9 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); CInlineAsmOperand::Symbol { symbol: fx.tcx.symbol_name(instance).name.to_owned() } } + InlineAsmOperand::Label { .. } => { + span_bug!(span, "asm! label operands are not yet supported"); + } }) .collect::>(); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6342802bb1960..c9a1e7789c27e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1119,6 +1119,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::InlineAsmOperand::SymStatic { def_id } => { InlineAsmOperandRef::SymStatic { def_id } } + mir::InlineAsmOperand::Label { target_index: _ } => { + todo!(); + } }) .collect(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 35058458eed7c..e058302af312c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -830,6 +830,9 @@ impl<'tcx> TerminatorKind<'tcx> { InlineAsmOperand::SymStatic { def_id } => { write!(fmt, "sym_static {def_id:?}")?; } + InlineAsmOperand::Label { target_index } => { + write!(fmt, "label {target_index}")?; + } } } write!(fmt, ", options({options:?}))") diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6f62c7186a768..f188923f87618 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -919,6 +919,10 @@ pub enum InlineAsmOperand<'tcx> { SymStatic { def_id: DefId, }, + Label { + /// This represents the index into the `targets` array in `TerminatorKind::InlineAsm`. + target_index: usize, + }, } /// Type for MIR `Assert` terminator error messages. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index a1418b41e057f..845b17175505d 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -595,7 +595,8 @@ macro_rules! make_mir_visitor { self.visit_constant(value, location); } InlineAsmOperand::Out { place: None, .. } - | InlineAsmOperand::SymStatic { def_id: _ } => {} + | InlineAsmOperand::SymStatic { def_id: _ } + | InlineAsmOperand::Label { target_index: _ } => {} } } } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index a49d864701a03..09e518606e314 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -400,6 +400,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { line_spans, }) => { use rustc_middle::{mir, thir}; + + let destination_block = this.cfg.start_new_block(); + let mut targets = if options.contains(InlineAsmOptions::NORETURN) { + vec![] + } else { + vec![destination_block] + }; + let operands = operands .into_iter() .map(|op| match *op { @@ -455,17 +463,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { thir::InlineAsmOperand::SymStatic { def_id } => { mir::InlineAsmOperand::SymStatic { def_id } } - thir::InlineAsmOperand::Label { .. } => { - todo!() + thir::InlineAsmOperand::Label { block } => { + let target = this.cfg.start_new_block(); + let target_index = targets.len(); + targets.push(target); + + let tmp = this.get_unit_temp(); + let target = unpack!(this.ast_block(tmp, target, block, source_info)); + this.cfg.terminate( + target, + source_info, + TerminatorKind::Goto { target: destination_block }, + ); + + mir::InlineAsmOperand::Label { target_index } } }) .collect(); - if !options.contains(InlineAsmOptions::NORETURN) { + if !expr.ty.is_never() { this.cfg.push_assign_unit(block, source_info, destination, this.tcx); } - let destination_block = this.cfg.start_new_block(); this.cfg.terminate( block, source_info, @@ -474,11 +493,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { operands, options, line_spans, - targets: if options.contains(InlineAsmOptions::NORETURN) { - Vec::new() - } else { - vec![destination_block] - }, + targets, unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) { UnwindAction::Continue } else { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 595c2ff5bf7fa..29169c31263bd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -271,7 +271,8 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { InlineAsmOperand::In { .. } | InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } => {} + | InlineAsmOperand::SymStatic { .. } + | InlineAsmOperand::Label { .. } => {} } } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 95f12301395c3..db48ecd702bdb 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -515,7 +515,8 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { } InlineAsmOperand::Const { value: _ } | InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} + | InlineAsmOperand::SymStatic { def_id: _ } + | InlineAsmOperand::Label { target_index: _ } => {} } } } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 2c8201b1903e6..10fea09531a65 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -648,7 +648,8 @@ impl WriteInfo { } InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } => (), + | InlineAsmOperand::SymStatic { .. } + | InlineAsmOperand::Label { .. } => {} } } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 92e86cf0ee41e..003a9a5920018 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -559,7 +559,8 @@ impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> { } InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } => (None, None), + | InlineAsmOperand::SymStatic { .. } + | InlineAsmOperand::Label { .. } => (None, None), }; stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{self:?}") } From 27e6ee102ed359a3744ac1f2dffbfb7a60bd2371 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 26 Dec 2023 04:20:35 +0000 Subject: [PATCH 11/27] Add callbr support to LLVM wrapper --- compiler/rustc_codegen_llvm/src/builder.rs | 52 +++++++++++++++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 14 +++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 25 +++++++++ 3 files changed, 91 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 8cab2a3f27c1b..eda3c583994a0 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1547,6 +1547,58 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } } + pub(crate) fn callbr( + &mut self, + llty: &'ll Type, + fn_attrs: Option<&CodegenFnAttrs>, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + llfn: &'ll Value, + args: &[&'ll Value], + default_dest: &'ll BasicBlock, + indirect_dest: &[&'ll BasicBlock], + funclet: Option<&Funclet<'ll>>, + ) -> &'ll Value { + debug!("invoke {:?} with args ({:?})", llfn, args); + + let args = self.check_call("callbr", llty, llfn, args); + let funclet_bundle = funclet.map(|funclet| funclet.bundle()); + let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw); + let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + if let Some(funclet_bundle) = funclet_bundle { + bundles.push(funclet_bundle); + } + + // Emit CFI pointer type membership test + self.cfi_type_test(fn_attrs, fn_abi, llfn); + + // Emit KCFI operand bundle + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); + if let Some(kcfi_bundle) = kcfi_bundle { + bundles.push(kcfi_bundle); + } + + let callbr = unsafe { + llvm::LLVMRustBuildCallBr( + self.llbuilder, + llty, + llfn, + default_dest, + indirect_dest.as_ptr(), + indirect_dest.len() as c_uint, + args.as_ptr(), + args.len() as c_uint, + bundles.as_ptr(), + bundles.len() as c_uint, + UNNAMED, + ) + }; + if let Some(fn_abi) = fn_abi { + fn_abi.apply_attrs_callsite(self, callbr); + } + callbr + } + // Emits CFI pointer type membership tests. fn cfi_type_test( &mut self, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index dbf35e5f49952..22c67436d3619 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1617,6 +1617,20 @@ extern "C" { Name: *const c_char, ) -> &'a Value; + pub fn LLVMRustBuildCallBr<'a>( + B: &Builder<'a>, + Ty: &'a Type, + Fn: &'a Value, + DefaultDest: &'a BasicBlock, + IndirectDests: *const &'a BasicBlock, + NumIndirectDests: c_uint, + Args: *const &'a Value, + NumArgs: c_uint, + OpBundles: *const &OperandBundleDef<'a>, + NumOpBundles: c_uint, + Name: *const c_char, + ) -> &'a Value; + pub fn LLVMRustSetFastMath(Instr: &Value); pub fn LLVMRustSetAlgebraicMath(Instr: &Value); pub fn LLVMRustSetAllowReassoc(Instr: &Value); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index af2353fbb19c5..63714de1fc64d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1539,6 +1539,31 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, Name)); } +extern "C" LLVMValueRef +LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, + LLVMBasicBlockRef DefaultDest, + LLVMBasicBlockRef *IndirectDests, unsigned NumIndirectDests, + LLVMValueRef *Args, unsigned NumArgs, + OperandBundleDef **OpBundles, unsigned NumOpBundles, + const char *Name) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = unwrap(Ty); + + // FIXME: Is there a way around this? + std::vector IndirectDestsUnwrapped; + IndirectDestsUnwrapped.reserve(NumIndirectDests); + for (unsigned i = 0; i < NumIndirectDests; ++i) { + IndirectDestsUnwrapped.push_back(unwrap(IndirectDests[i])); + } + + return wrap(unwrap(B)->CreateCallBr( + FTy, Callee, unwrap(DefaultDest), + ArrayRef(IndirectDestsUnwrapped), + ArrayRef(unwrap(Args), NumArgs), + ArrayRef(*OpBundles, NumOpBundles), + Name)); +} + extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, LLVMBasicBlockRef BB) { auto Point = unwrap(BB)->getFirstInsertionPt(); From 5e4fd6bc2301a8e267d044a526ffb77c1a6c35d7 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 27 Dec 2023 04:08:04 +0000 Subject: [PATCH 12/27] Implement asm goto for LLVM and GCC backend --- compiler/rustc_codegen_gcc/src/asm.rs | 31 ++++++++++++++-- compiler/rustc_codegen_llvm/src/asm.rs | 29 +++++++++------ compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 ++ compiler/rustc_codegen_ssa/src/mir/block.rs | 38 +++++++++++++++----- compiler/rustc_codegen_ssa/src/traits/asm.rs | 6 +++- 5 files changed, 84 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 78e8e32b97299..07edd26e27a42 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -107,7 +107,7 @@ enum ConstraintOrRegister { impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { - fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) { + fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], instance: Instance<'_>, dest: Option, _catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>) { if options.contains(InlineAsmOptions::MAY_UNWIND) { self.sess().dcx() .create_err(UnwindingInlineAsm { span: span[0] }) @@ -126,6 +126,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // added to `outputs.len()` let mut inputs = vec![]; + // GCC index of a label equals its position in the array added to + // `outputs.len() + inputs.len()`. + let mut labels = vec![]; + // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _` let mut clobbers = vec![]; @@ -269,6 +273,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // some targets to add a leading underscore (Mach-O). constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len(); } + + InlineAsmOperandRef::Label { label } => { + labels.push(label); + } } } @@ -368,6 +376,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { InlineAsmOperandRef::Const { .. } => { // processed in the previous pass } + + InlineAsmOperandRef::Label { .. } => { + // processed in the previous pass + } } } @@ -454,6 +466,14 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { InlineAsmOperandRef::Const { ref string } => { template_str.push_str(string); } + + InlineAsmOperandRef::Label { label } => { + let label_gcc_index = labels.iter() + .position(|&l| l == label) + .expect("wrong rust index"); + let gcc_index = label_gcc_index + outputs.len() + inputs.len(); + push_to_template(Some('l'), gcc_index); + } } } } @@ -466,7 +486,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // 4. Generate Extended Asm block let block = self.llbb(); - let extended_asm = block.add_extended_asm(None, &template_str); + let extended_asm = if let Some(dest) = dest { + assert!(!labels.is_empty()); + block.end_with_extended_asm_goto(None, &template_str, &labels, Some(dest)) + } else { + block.add_extended_asm(None, &template_str) + }; for op in &outputs { extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var); @@ -494,7 +519,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { if !options.contains(InlineAsmOptions::NOSTACK) { // TODO(@Commeownist): figure out how to align stack } - if options.contains(InlineAsmOptions::NORETURN) { + if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) { let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable"); let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; self.call(self.type_void(), None, None, builtin_unreachable, &[], None); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index a413466093bed..f5d0b3c465729 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -28,7 +28,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { options: InlineAsmOptions, line_spans: &[Span], instance: Instance<'_>, - dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>, + dest: Option, + catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>, ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -165,6 +166,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } // Build the template string + let mut labels = vec![]; let mut template_str = String::new(); for piece in template { match *piece { @@ -205,6 +207,11 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // Only emit the raw symbol name template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); } + InlineAsmOperandRef::Label { label } => { + template_str.push_str(&format!("${{{}:l}}", constraints.len())); + constraints.push("!i".to_owned()); + labels.push(label); + } } } } @@ -292,12 +299,14 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &constraints.join(","), &inputs, output_type, + &labels, volatile, alignstack, dialect, line_spans, options.contains(InlineAsmOptions::MAY_UNWIND), - dest_catch_funclet, + dest, + catch_funclet, ) .unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed")); @@ -317,7 +326,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs }); // Switch to the 'normal' basic block if we did an `invoke` instead of a `call` - if let Some((dest, _, _)) = dest_catch_funclet { + if let Some(dest) = dest { self.switch_to_block(dest); } @@ -415,16 +424,14 @@ pub(crate) fn inline_asm_call<'ll>( cons: &str, inputs: &[&'ll Value], output: &'ll llvm::Type, + labels: &[&'ll llvm::BasicBlock], volatile: bool, alignstack: bool, dia: llvm::AsmDialect, line_spans: &[Span], unwind: bool, - dest_catch_funclet: Option<( - &'ll llvm::BasicBlock, - &'ll llvm::BasicBlock, - Option<&Funclet<'ll>>, - )>, + dest: Option<&'ll llvm::BasicBlock>, + catch_funclet: Option<(&'ll llvm::BasicBlock, Option<&Funclet<'ll>>)>, ) -> Option<&'ll Value> { let volatile = if volatile { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False }; @@ -457,8 +464,10 @@ pub(crate) fn inline_asm_call<'ll>( can_throw, ); - let call = if let Some((dest, catch, funclet)) = dest_catch_funclet { - bx.invoke(fty, None, None, v, inputs, dest, catch, funclet) + let call = if !labels.is_empty() { + bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None) + } else if let Some((catch, funclet)) = catch_funclet { + bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet) } else { bx.call(fty, None, None, v, inputs, None) }; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 23e6f054a7c1e..ee76038800fd3 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -377,12 +377,14 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { constraint, inputs, self.type_void(), + &[], true, false, llvm::AsmDialect::Att, &[span], false, None, + None, ) .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`")); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index c9a1e7789c27e..2eba489cbfbf8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -264,7 +264,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mir::UnwindAction::Unreachable => None, }; - if let Some(cleanup) = unwind_target { + if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) { let ret_llbb = if let Some(target) = destination { fx.llbb(target) } else { @@ -277,11 +277,29 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { options, line_spans, instance, - Some((ret_llbb, cleanup, self.funclet(fx))), + Some(ret_llbb), + None, + ); + MergingSucc::False + } else if let Some(cleanup) = unwind_target { + let ret_llbb = if let Some(target) = destination { + fx.llbb(target) + } else { + fx.unreachable_block() + }; + + bx.codegen_inline_asm( + template, + operands, + options, + line_spans, + instance, + Some(ret_llbb), + Some((cleanup, self.funclet(fx))), ); MergingSucc::False } else { - bx.codegen_inline_asm(template, operands, options, line_spans, instance, None); + bx.codegen_inline_asm(template, operands, options, line_spans, instance, None, None); if let Some(target) = destination { self.funclet_br(fx, bx, target, mergeable_succ) @@ -1067,7 +1085,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operands: &[mir::InlineAsmOperand<'tcx>], options: ast::InlineAsmOptions, line_spans: &[Span], - destination: Option, + targets: &[mir::BasicBlock], unwind: mir::UnwindAction, instance: Instance<'_>, mergeable_succ: bool, @@ -1119,8 +1137,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::InlineAsmOperand::SymStatic { def_id } => { InlineAsmOperandRef::SymStatic { def_id } } - mir::InlineAsmOperand::Label { target_index: _ } => { - todo!(); + mir::InlineAsmOperand::Label { target_index } => { + InlineAsmOperandRef::Label { label: self.llbb(targets[target_index]) } } }) .collect(); @@ -1132,7 +1150,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &operands, options, line_spans, - destination, + if options.contains(InlineAsmOptions::NORETURN) { + None + } else { + targets.get(0).copied() + }, unwind, instance, mergeable_succ, @@ -1298,7 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operands, options, line_spans, - targets.get(0).copied(), + targets, unwind, self.instance, mergeable_succ(), diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index c2ae74b18d81e..8d67b626bbdb5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -33,6 +33,9 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { SymStatic { def_id: DefId, }, + Label { + label: B::BasicBlock, + }, } #[derive(Debug)] @@ -51,7 +54,8 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { options: InlineAsmOptions, line_spans: &[Span], instance: Instance<'_>, - dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>, + dest: Option, + catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>, ); } From 5e4e3d790bb5e53b31abf58fd0e2be557c28d496 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 26 Dec 2023 04:44:01 +0000 Subject: [PATCH 13/27] Ensure asm noreturn works with labels --- compiler/rustc_hir_typeck/src/expr.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 614761c03bd61..ba74476c36dc7 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3243,6 +3243,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { + let mut diverge = asm.options.contains(ast::InlineAsmOptions::NORETURN); + for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::In { expr, .. } => { @@ -3265,15 +3267,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} hir::InlineAsmOperand::SymStatic { .. } => {} hir::InlineAsmOperand::Label { block } => { - self.check_block_no_value(block); + let previous_diverges = self.diverges.get(); + + // The label blocks should have unit return value or diverge. + let ty = + self.check_block_with_expected(block, ExpectHasType(self.tcx.types.unit)); + if !ty.is_never() { + self.demand_suptype(block.span, self.tcx.types.unit, ty); + diverge = false; + } + + // We need this to avoid false unreachable warning when a label diverges. + self.diverges.set(previous_diverges); } } } - if asm.options.contains(ast::InlineAsmOptions::NORETURN) { - self.tcx.types.never - } else { - Ty::new_unit(self.tcx) - } + + if diverge { self.tcx.types.never } else { self.tcx.types.unit } } fn check_offset_of( From 31f078ea9941ce89f1f5ab5fb1239bbbddfe4d49 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 26 Dec 2023 04:44:22 +0000 Subject: [PATCH 14/27] Forbid asm unwind to work with labels --- compiler/rustc_builtin_macros/messages.ftl | 2 ++ compiler/rustc_builtin_macros/src/asm.rs | 7 +++++++ compiler/rustc_builtin_macros/src/errors.rs | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index dda466b026d91..32e065ada51b1 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -19,6 +19,8 @@ builtin_macros_asm_expected_other = expected operand, {$is_global_asm -> builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names +builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option + builtin_macros_asm_modifier_invalid = asm template modifier must be a single character builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 93eb3a9a43ee3..85f009f7e63cd 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -245,6 +245,7 @@ pub fn parse_asm_args<'a>( let mut have_real_output = false; let mut outputs_sp = vec![]; let mut regclass_outputs = vec![]; + let mut labels_sp = vec![]; for (op, op_sp) in &args.operands { match op { ast::InlineAsmOperand::Out { reg, expr, .. } @@ -262,6 +263,9 @@ pub fn parse_asm_args<'a>( regclass_outputs.push(*op_sp); } } + ast::InlineAsmOperand::Label { .. } => { + labels_sp.push(*op_sp); + } _ => {} } } @@ -273,6 +277,9 @@ pub fn parse_asm_args<'a>( // Bail out now since this is likely to confuse MIR return Err(err); } + if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { + dcx.emit_err(errors::AsmMayUnwind { labels_sp }); + } if args.clobber_abis.len() > 0 { if is_global_asm { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index f304a37be854b..51f7fcd628bc0 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -787,6 +787,13 @@ pub(crate) struct AsmNoReturn { pub(crate) outputs_sp: Vec, } +#[derive(Diagnostic)] +#[diag(builtin_macros_asm_mayunwind)] +pub(crate) struct AsmMayUnwind { + #[primary_span] + pub(crate) labels_sp: Vec, +} + #[derive(Diagnostic)] #[diag(builtin_macros_global_asm_clobber_abi)] pub(crate) struct GlobalAsmClobberAbi { From 4677a71369f690b5aecf9380cf12074792f09a3f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 27 Dec 2023 22:57:17 +0000 Subject: [PATCH 15/27] Add tests for asm goto --- tests/codegen/asm-goto.rs | 51 ++++++++ tests/ui/asm/parse-error.rs | 2 + tests/ui/asm/parse-error.stderr | 16 ++- tests/ui/asm/x86_64/bad-options.rs | 4 + tests/ui/asm/x86_64/bad-options.stderr | 38 +++--- tests/ui/asm/x86_64/goto.mirunsafeck.stderr | 23 ++++ tests/ui/asm/x86_64/goto.rs | 111 ++++++++++++++++++ tests/ui/asm/x86_64/goto.thirunsafeck.stderr | 23 ++++ .../ui/feature-gates/feature-gate-asm_goto.rs | 10 ++ .../feature-gate-asm_goto.stderr | 13 ++ 10 files changed, 270 insertions(+), 21 deletions(-) create mode 100644 tests/codegen/asm-goto.rs create mode 100644 tests/ui/asm/x86_64/goto.mirunsafeck.stderr create mode 100644 tests/ui/asm/x86_64/goto.rs create mode 100644 tests/ui/asm/x86_64/goto.thirunsafeck.stderr create mode 100644 tests/ui/feature-gates/feature-gate-asm_goto.rs create mode 100644 tests/ui/feature-gates/feature-gate-asm_goto.stderr diff --git a/tests/codegen/asm-goto.rs b/tests/codegen/asm-goto.rs new file mode 100644 index 0000000000000..e522d0da5b405 --- /dev/null +++ b/tests/codegen/asm-goto.rs @@ -0,0 +1,51 @@ +//@ compile-flags: -O +//@ only-x86_64 + +#![crate_type = "rlib"] +#![feature(asm_goto)] + +use std::arch::asm; + +#[no_mangle] +pub extern "C" fn panicky() {} + +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) { + println!(); + } +} + +// CHECK-LABEL: @asm_goto +#[no_mangle] +pub unsafe fn asm_goto() { + // CHECK: callbr void asm sideeffect alignstack inteldialect " + // CHECK-NEXT: to label %[[FALLTHROUGHBB:[a-b0-9]+]] [label %[[JUMPBB:[a-b0-9]+]]] + asm!("jmp {}", label {}); +} + +// CHECK-LABEL: @asm_goto_with_outputs +#[no_mangle] +pub unsafe fn asm_goto_with_outputs() -> u64 { + let out: u64; + // CHECK: [[RES:%[0-9]+]] = callbr i64 asm sideeffect alignstack inteldialect " + // CHECK-NEXT: to label %[[FALLTHROUGHBB:[a-b0-9]+]] [label %[[JUMPBB:[a-b0-9]+]]] + asm!("{} /* {} */", out(reg) out, label { return 1; }); + // CHECK: [[JUMPBB]]: + // CHECK-NEXT: [[RET:%.+]] = phi i64 [ [[RES]], %[[FALLTHROUGHBB]] ], [ 1, %start ] + // CHECK-NEXT: ret i64 [[RET]] + out +} + +// CHECK-LABEL: @asm_goto_noreturn +#[no_mangle] +pub unsafe fn asm_goto_noreturn() -> u64 { + let out: u64; + // CHECK: callbr void asm sideeffect alignstack inteldialect " + // CHECK-NEXT: to label %unreachable [label %[[JUMPBB:[a-b0-9]+]]] + asm!("jmp {}", label { return 1; }, options(noreturn)); + // CHECK: [[JUMPBB]]: + // CHECK-NEXT: ret i64 1 + out +} diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs index 6f32293511bf6..a0251c6763ba4 100644 --- a/tests/ui/asm/parse-error.rs +++ b/tests/ui/asm/parse-error.rs @@ -142,3 +142,5 @@ global_asm!(format!("{{{}}}", 0), const FOO); //~^ ERROR asm template must be a string literal global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); //~^ ERROR asm template must be a string literal +global_asm!("{}", label {}); +//~^ ERROR expected operand, options, or additional template string diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 075d28e176ad8..80ee5191dbbe9 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -176,17 +176,17 @@ LL | asm!("{a}", a = const foo, a = const bar); | = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` +error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` --> $DIR/parse-error.rs:82:29 | LL | asm!("", options(), ""); - | ^^ expected one of 9 possible tokens + | ^^ expected one of 10 possible tokens -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` +error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` --> $DIR/parse-error.rs:84:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); - | ^^^^ expected one of 9 possible tokens + | ^^^^ expected one of 10 possible tokens error: asm template must be a string literal --> $DIR/parse-error.rs:86:14 @@ -362,6 +362,12 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) +error: expected operand, options, or additional template string + --> $DIR/parse-error.rs:145:19 + | +LL | global_asm!("{}", label {}); + | ^^^^^^^^ expected operand, options, or additional template string + error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:39:37 | @@ -407,6 +413,6 @@ LL | let mut bar = 0; LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value -error: aborting due to 63 previous errors +error: aborting due to 64 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/x86_64/bad-options.rs b/tests/ui/asm/x86_64/bad-options.rs index a6d5022ecf130..f9cc13cfc5a8b 100644 --- a/tests/ui/asm/x86_64/bad-options.rs +++ b/tests/ui/asm/x86_64/bad-options.rs @@ -1,5 +1,7 @@ //@ only-x86_64 +#![feature(asm_unwind, asm_goto)] + use std::arch::{asm, global_asm}; fn main() { @@ -14,6 +16,8 @@ fn main() { //~^ ERROR asm with the `pure` option must have at least one output asm!("{}", out(reg) foo, options(noreturn)); //~^ ERROR asm outputs are not allowed with the `noreturn` option + asm!("{}", label {}, options(may_unwind)); + //~^ ERROR asm labels are not allowed with the `may_unwind` option } unsafe { diff --git a/tests/ui/asm/x86_64/bad-options.stderr b/tests/ui/asm/x86_64/bad-options.stderr index e2351840eef21..aa167e7913c3e 100644 --- a/tests/ui/asm/x86_64/bad-options.stderr +++ b/tests/ui/asm/x86_64/bad-options.stderr @@ -1,35 +1,41 @@ error: the `nomem` and `readonly` options are mutually exclusive - --> $DIR/bad-options.rs:8:18 + --> $DIR/bad-options.rs:10:18 | LL | asm!("", options(nomem, readonly)); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: the `pure` and `noreturn` options are mutually exclusive - --> $DIR/bad-options.rs:10:18 + --> $DIR/bad-options.rs:12:18 | LL | asm!("", options(pure, nomem, noreturn)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm with the `pure` option must have at least one output - --> $DIR/bad-options.rs:10:18 + --> $DIR/bad-options.rs:12:18 | LL | asm!("", options(pure, nomem, noreturn)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm with the `pure` option must have at least one output - --> $DIR/bad-options.rs:13:33 + --> $DIR/bad-options.rs:15:33 | LL | asm!("{}", in(reg) foo, options(pure, nomem)); | ^^^^^^^^^^^^^^^^^^^^ error: asm outputs are not allowed with the `noreturn` option - --> $DIR/bad-options.rs:15:20 + --> $DIR/bad-options.rs:17:20 | LL | asm!("{}", out(reg) foo, options(noreturn)); | ^^^^^^^^^^^^ +error: asm labels are not allowed with the `may_unwind` option + --> $DIR/bad-options.rs:19:20 + | +LL | asm!("{}", label {}, options(may_unwind)); + | ^^^^^^^^ + error: asm with `clobber_abi` must specify explicit registers for outputs - --> $DIR/bad-options.rs:22:20 + --> $DIR/bad-options.rs:26:20 | LL | asm!("{}", out(reg) foo, clobber_abi("C")); | ^^^^^^^^^^^^ ---------------- clobber_abi @@ -37,7 +43,7 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C")); | generic outputs error: asm with `clobber_abi` must specify explicit registers for outputs - --> $DIR/bad-options.rs:24:20 + --> $DIR/bad-options.rs:28:20 | LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^ ---------------- ---------------- clobber_abi @@ -46,43 +52,43 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C")); | generic outputs error: expected one of `)`, `att_syntax`, or `raw`, found `nomem` - --> $DIR/bad-options.rs:31:25 + --> $DIR/bad-options.rs:35:25 | LL | global_asm!("", options(nomem)); | ^^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `readonly` - --> $DIR/bad-options.rs:33:25 + --> $DIR/bad-options.rs:37:25 | LL | global_asm!("", options(readonly)); | ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn` - --> $DIR/bad-options.rs:35:25 + --> $DIR/bad-options.rs:39:25 | LL | global_asm!("", options(noreturn)); | ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `pure` - --> $DIR/bad-options.rs:37:25 + --> $DIR/bad-options.rs:41:25 | LL | global_asm!("", options(pure)); | ^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `nostack` - --> $DIR/bad-options.rs:39:25 + --> $DIR/bad-options.rs:43:25 | LL | global_asm!("", options(nostack)); | ^^^^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags` - --> $DIR/bad-options.rs:41:25 + --> $DIR/bad-options.rs:45:25 | LL | global_asm!("", options(preserves_flags)); | ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw` error: invalid ABI for `clobber_abi` - --> $DIR/bad-options.rs:20:18 + --> $DIR/bad-options.rs:24:18 | LL | asm!("", clobber_abi("foo")); | ^^^^^^^^^^^^^^^^^^ @@ -90,12 +96,12 @@ LL | asm!("", clobber_abi("foo")); = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` error: `C` ABI specified multiple times - --> $DIR/bad-options.rs:24:52 + --> $DIR/bad-options.rs:28:52 | LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C")); | ---------------- ^^^^^^^^^^^^^^^^ | | | previously specified here -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/asm/x86_64/goto.mirunsafeck.stderr b/tests/ui/asm/x86_64/goto.mirunsafeck.stderr new file mode 100644 index 0000000000000..fe189c14f0a7c --- /dev/null +++ b/tests/ui/asm/x86_64/goto.mirunsafeck.stderr @@ -0,0 +1,23 @@ +warning: unreachable statement + --> $DIR/goto.rs:99:9 + | +LL | / asm!( +LL | | "jmp {}", +LL | | label { +LL | | return; +LL | | }, +LL | | options(noreturn) +LL | | ); + | |_________- any code following this expression is unreachable +LL | unreachable!(); + | ^^^^^^^^^^^^^^ unreachable statement + | +note: the lint level is defined here + --> $DIR/goto.rs:89:8 + | +LL | #[warn(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this warning originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/asm/x86_64/goto.rs b/tests/ui/asm/x86_64/goto.rs new file mode 100644 index 0000000000000..6a567efbb2c7e --- /dev/null +++ b/tests/ui/asm/x86_64/goto.rs @@ -0,0 +1,111 @@ +//@ only-x86_64 +//@ run-pass +//@ needs-asm-support +//@ revisions: mirunsafeck thirunsafeck +//@ [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![deny(unreachable_code)] +#![feature(asm_goto)] + +use std::arch::asm; + +fn goto_fallthough() { + unsafe { + asm!( + "/* {} */", + label { + unreachable!(); + } + ) + } +} + +fn goto_jump() { + unsafe { + let mut value = false; + asm!( + "jmp {}", + label { + value = true; + } + ); + assert!(value); + } +} + +// asm goto with outputs cause miscompilation in LLVM. UB can be triggered +// when outputs are used inside the label block when optimisation is enabled. +// See: https://github.com/llvm/llvm-project/issues/74483 +/* +fn goto_out_fallthrough() { + unsafe { + let mut out: usize; + asm!( + "lea {}, [{} + 1]", + "/* {} */", + out(reg) out, + in(reg) 0x12345678usize, + label { + unreachable!(); + } + ); + assert_eq!(out, 0x12345679); + } +} + +fn goto_out_jump() { + unsafe { + let mut value = false; + let mut out: usize; + asm!( + "lea {}, [{} + 1]", + "jmp {}", + out(reg) out, + in(reg) 0x12345678usize, + label { + value = true; + assert_eq!(out, 0x12345679); + } + ); + assert!(value); + } +} +*/ + +fn goto_noreturn() { + unsafe { + let a; + asm!( + "jmp {}", + label { + a = 1; + }, + options(noreturn) + ); + assert_eq!(a, 1); + } +} + +#[warn(unreachable_code)] +fn goto_noreturn_diverge() { + unsafe { + asm!( + "jmp {}", + label { + return; + }, + options(noreturn) + ); + unreachable!(); + //~^ WARN unreachable statement + } +} + +fn main() { + goto_fallthough(); + goto_jump(); + // goto_out_fallthrough(); + // goto_out_jump(); + goto_noreturn(); + goto_noreturn_diverge(); +} diff --git a/tests/ui/asm/x86_64/goto.thirunsafeck.stderr b/tests/ui/asm/x86_64/goto.thirunsafeck.stderr new file mode 100644 index 0000000000000..fe189c14f0a7c --- /dev/null +++ b/tests/ui/asm/x86_64/goto.thirunsafeck.stderr @@ -0,0 +1,23 @@ +warning: unreachable statement + --> $DIR/goto.rs:99:9 + | +LL | / asm!( +LL | | "jmp {}", +LL | | label { +LL | | return; +LL | | }, +LL | | options(noreturn) +LL | | ); + | |_________- any code following this expression is unreachable +LL | unreachable!(); + | ^^^^^^^^^^^^^^ unreachable statement + | +note: the lint level is defined here + --> $DIR/goto.rs:89:8 + | +LL | #[warn(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this warning originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/feature-gates/feature-gate-asm_goto.rs b/tests/ui/feature-gates/feature-gate-asm_goto.rs new file mode 100644 index 0000000000000..beac4590349f6 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_goto.rs @@ -0,0 +1,10 @@ +//@ only-x86_64 + +use std::arch::asm; + +fn main() { + unsafe { + asm!("jmp {}", label {}); + //~^ ERROR label operands for inline assembly are unstable + } +} diff --git a/tests/ui/feature-gates/feature-gate-asm_goto.stderr b/tests/ui/feature-gates/feature-gate-asm_goto.stderr new file mode 100644 index 0000000000000..62fd1a320d3c0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_goto.stderr @@ -0,0 +1,13 @@ +error[E0658]: label operands for inline assembly are unstable + --> $DIR/feature-gate-asm_goto.rs:7:24 + | +LL | asm!("jmp {}", label {}); + | ^^^^^^^^ + | + = note: see issue #119364 for more information + = help: add `#![feature(asm_goto)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From 84bc9e9e369ba23a244d9a6b8c047e6f1e0d6e7a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 28 Dec 2023 18:57:15 +0000 Subject: [PATCH 16/27] Add asm-goto to unstable book --- .../src/language-features/asm-goto.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/asm-goto.md diff --git a/src/doc/unstable-book/src/language-features/asm-goto.md b/src/doc/unstable-book/src/language-features/asm-goto.md new file mode 100644 index 0000000000000..a9ad5e837448a --- /dev/null +++ b/src/doc/unstable-book/src/language-features/asm-goto.md @@ -0,0 +1,24 @@ +# `asm_goto` + +The tracking issue for this feature is: [#119364] + +[#119364]: https://github.com/rust-lang/rust/issues/119364 + +------------------------ + +This feature adds a `label ` operand type to `asm!`. + +Example: +```rust,ignore (partial-example, x86-only) + +unsafe { + asm!( + "jmp {}", + label { + println!("Jumped from asm!"); + } + ); +} +``` + +The block must have unit type. From 626a5f589208d544537f411724f35e6b841790e4 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 24 Feb 2024 17:12:25 +0000 Subject: [PATCH 17/27] Add assertions and clarify asm-goto with noreturn --- compiler/rustc_codegen_llvm/src/asm.rs | 1 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + src/doc/unstable-book/src/language-features/asm-goto.md | 8 +++++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index f5d0b3c465729..74539d4d49570 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -465,6 +465,7 @@ pub(crate) fn inline_asm_call<'ll>( ); let call = if !labels.is_empty() { + assert!(catch_funclet.is_none()); bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None) } else if let Some((catch, funclet)) = catch_funclet { bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 2eba489cbfbf8..8a2f91c082e27 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -265,6 +265,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { }; if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) { + assert!(unwind_target.is_none()); let ret_llbb = if let Some(target) = destination { fx.llbb(target) } else { diff --git a/src/doc/unstable-book/src/language-features/asm-goto.md b/src/doc/unstable-book/src/language-features/asm-goto.md index a9ad5e837448a..d72eb7c0c6ef1 100644 --- a/src/doc/unstable-book/src/language-features/asm-goto.md +++ b/src/doc/unstable-book/src/language-features/asm-goto.md @@ -21,4 +21,10 @@ unsafe { } ``` -The block must have unit type. +The block must have unit type or diverge. + +When `label ` is used together with `noreturn` option, it means that the +assembly will not fallthrough. It's allowed to jump to a label within the +assembly. In this case, the entire `asm!` expression will have an unit type as +opposed to diverging, if not all label blocks diverge. The `asm!` expression +still diverges if `noreturn` option is used and all label blocks diverge. From 8212fc513c66ebd9996180456305ecd6c425d5da Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 3 Mar 2024 12:57:28 +0100 Subject: [PATCH 18/27] Fix quadratic behavior of repeated vectored writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some implementations of `Write::write_vectored` in the standard library (`BufWriter`, `LineWriter`, `Stdout`, `Stderr`) check all buffers to calculate the total length. This is O(n) over the number of buffers. It's common that only a limited number of buffers is written at a time (e.g. 1024 for `writev(2)`). `write_vectored_all` will then call `write_vectored` repeatedly, leading to a runtime of O(n²) over the number of buffers. The fix is to only calculate as much as needed if it's needed. --- library/std/src/io/buffered/bufwriter.rs | 51 ++++++++++--------- library/std/src/io/buffered/linewritershim.rs | 15 ++++-- library/std/src/io/stdio.rs | 15 ++++-- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 665d8602c0800..2d13230ffbabd 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -554,35 +554,38 @@ impl Write for BufWriter { // same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the // computation overflows, then surely the input cannot fit in our buffer, so we forward // to the inner writer's `write_vectored` method to let it handle it appropriately. - let saturated_total_len = - bufs.iter().fold(0usize, |acc, b| acc.saturating_add(b.len())); + let mut saturated_total_len: usize = 0; - if saturated_total_len > self.spare_capacity() { - // Flush if the total length of the input exceeds our buffer's spare capacity. - // If we would have overflowed, this condition also holds, and we need to flush. - self.flush_buf()?; + for buf in bufs { + saturated_total_len = saturated_total_len.saturating_add(buf.len()); + + if saturated_total_len > self.spare_capacity() && !self.buf.is_empty() { + // Flush if the total length of the input exceeds our buffer's spare capacity. + // If we would have overflowed, this condition also holds, and we need to flush. + self.flush_buf()?; + } + + if saturated_total_len >= self.buf.capacity() { + // Forward to our inner writer if the total length of the input is greater than or + // equal to our buffer capacity. If we would have overflowed, this condition also + // holds, and we punt to the inner writer. + self.panicked = true; + let r = self.get_mut().write_vectored(bufs); + self.panicked = false; + return r; + } } - if saturated_total_len >= self.buf.capacity() { - // Forward to our inner writer if the total length of the input is greater than or - // equal to our buffer capacity. If we would have overflowed, this condition also - // holds, and we punt to the inner writer. - self.panicked = true; - let r = self.get_mut().write_vectored(bufs); - self.panicked = false; - r - } else { - // `saturated_total_len < self.buf.capacity()` implies that we did not saturate. + // `saturated_total_len < self.buf.capacity()` implies that we did not saturate. - // SAFETY: We checked whether or not the spare capacity was large enough above. If - // it was, then we're safe already. If it wasn't, we flushed, making sufficient - // room for any input <= the buffer size, which includes this input. - unsafe { - bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b)); - }; + // SAFETY: We checked whether or not the spare capacity was large enough above. If + // it was, then we're safe already. If it wasn't, we flushed, making sufficient + // room for any input <= the buffer size, which includes this input. + unsafe { + bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b)); + }; - Ok(saturated_total_len) - } + Ok(saturated_total_len) } else { let mut iter = bufs.iter(); let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) { diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index 7eadfd413661d..c3ac7855d4450 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -175,6 +175,10 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { } // Find the buffer containing the last newline + // FIXME: This is overly slow if there are very many bufs and none contain + // newlines. e.g. writev() on Linux only writes up to 1024 slices, so + // scanning the rest is wasted effort. This makes write_all_vectored() + // quadratic. let last_newline_buf_idx = bufs .iter() .enumerate() @@ -215,9 +219,14 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { // Don't try to reconstruct the exact amount written; just bail // in the event of a partial write - let lines_len = lines.iter().map(|buf| buf.len()).sum(); - if flushed < lines_len { - return Ok(flushed); + let mut lines_len: usize = 0; + for buf in lines { + // With overlapping/duplicate slices the total length may in theory + // exceed usize::MAX + lines_len = lines_len.saturating_add(buf.len()); + if flushed < lines_len { + return Ok(flushed); + } } // Now that the write has succeeded, buffer the rest (or as much of the diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 455de6d98bae6..42fc0053030af 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -128,8 +128,8 @@ impl Write for StdoutRaw { } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = bufs.iter().map(|b| b.len()).sum(); - handle_ebadf(self.0.write_vectored(bufs), total) + let total = || bufs.iter().map(|b| b.len()).sum(); + handle_ebadf_lazy(self.0.write_vectored(bufs), total) } #[inline] @@ -160,8 +160,8 @@ impl Write for StderrRaw { } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = bufs.iter().map(|b| b.len()).sum(); - handle_ebadf(self.0.write_vectored(bufs), total) + let total = || bufs.iter().map(|b| b.len()).sum(); + handle_ebadf_lazy(self.0.write_vectored(bufs), total) } #[inline] @@ -193,6 +193,13 @@ fn handle_ebadf(r: io::Result, default: T) -> io::Result { } } +fn handle_ebadf_lazy(r: io::Result, default: impl FnOnce() -> T) -> io::Result { + match r { + Err(ref e) if stdio::is_ebadf(e) => Ok(default()), + r => r, + } +} + /// A handle to the standard input stream of a process. /// /// Each handle is a shared reference to a global buffer of input data to this From d756375234fadad5471660fd24ae2d1758611a27 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 5 Jun 2023 19:51:24 +0800 Subject: [PATCH 19/27] Add new Tier-3 target: `loongarch64-unknown-linux-musl` MCP: https://github.com/rust-lang/compiler-team/issues/518 --- compiler/rustc_target/src/spec/mod.rs | 1 + .../targets/loongarch64_unknown_linux_musl.rs | 19 +++++++++++++++++++ src/doc/rustc/src/platform-support.md | 1 + 3 files changed, 21 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3fda1e1833c12..d053d69ba0250 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1404,6 +1404,7 @@ supported_targets! { ("i686-unknown-linux-gnu", i686_unknown_linux_gnu), ("i586-unknown-linux-gnu", i586_unknown_linux_gnu), ("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu), + ("loongarch64-unknown-linux-musl", loongarch64_unknown_linux_musl), ("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu), ("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2), ("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs new file mode 100644 index 0000000000000..9f653174cf0a8 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -0,0 +1,19 @@ +use crate::spec::{base, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "loongarch64-unknown-linux-musl".into(), + description: None, + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + arch: "loongarch64".into(), + options: TargetOptions { + cpu: "generic".into(), + features: "+f,+d".into(), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + crt_static_default: false, + ..base::linux_musl::opts() + }, + } +} diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 4c4bbd29ac66f..7face9bc60df7 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -169,6 +169,7 @@ target | std | notes `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI +[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] From 95e38470b79d31a83d1bae228ea515efd25d7e16 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 1 Mar 2024 11:43:27 +0800 Subject: [PATCH 20/27] tests: Add loongarch64-unknown-linux-musl target --- tests/assembly/targets/targets-elf.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index 4af94730b7d85..b0f8ebd5920b5 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -234,6 +234,9 @@ //@ revisions: loongarch64_unknown_linux_gnu //@ [loongarch64_unknown_linux_gnu] compile-flags: --target loongarch64-unknown-linux-gnu //@ [loongarch64_unknown_linux_gnu] needs-llvm-components: loongarch +//@ revisions: loongarch64_unknown_linux_musl +//@ [loongarch64_unknown_linux_musl] compile-flags: --target loongarch64-unknown-linux-musl +//@ [loongarch64_unknown_linux_musl] needs-llvm-components: loongarch //@ revisions: loongarch64_unknown_none //@ [loongarch64_unknown_none] compile-flags: --target loongarch64-unknown-none //@ [loongarch64_unknown_none] needs-llvm-components: loongarch From 36d271fd9cf0994dc69d3a0b803f08ff8dc17809 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 5 Mar 2024 09:59:32 -0600 Subject: [PATCH 21/27] Update src/doc/rustc/src/platform-support.md --- src/doc/rustc/src/platform-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7face9bc60df7..9c8998732433f 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -169,7 +169,7 @@ target | std | notes `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI -[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) +[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) with musl 1.2.3 [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] From cc38c1e9cb75d12f41cfda3317867759ab895b20 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 6 Mar 2024 19:14:03 +0100 Subject: [PATCH 22/27] Add #[inline] to BTreeMap::new constructor --- library/alloc/src/collections/btree/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 58ffa9710d3e6..e99c6220e2064 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -624,6 +624,7 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")] + #[inline] #[must_use] pub const fn new() -> BTreeMap { BTreeMap { root: None, length: 0, alloc: ManuallyDrop::new(Global), _marker: PhantomData } From 0ee0f290a61c973a014d44ca3f5e3dc165f5e562 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 25 Feb 2024 20:43:06 +0000 Subject: [PATCH 23/27] Bless aarch64 asm test --- tests/ui/asm/aarch64/parse-error.stderr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index 46984a1fe1ca0..539c134472fda 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -130,17 +130,17 @@ LL | asm!("{1}", in("x0") foo, const bar); | | | explicit register argument -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` +error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` --> $DIR/parse-error.rs:66:29 | LL | asm!("", options(), ""); - | ^^ expected one of 9 possible tokens + | ^^ expected one of 10 possible tokens -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` +error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` --> $DIR/parse-error.rs:68:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); - | ^^^^ expected one of 9 possible tokens + | ^^^^ expected one of 10 possible tokens error: asm template must be a string literal --> $DIR/parse-error.rs:70:14 From ef626d772f7ee9ddba526b0693993f6479ee5c14 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Thu, 7 Mar 2024 09:49:15 -0500 Subject: [PATCH 24/27] PassWrapper: update for llvm/llvm-project@a3319371970b @rustbot label: +llvm-main --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 1cdfc22431e3e..f6253068eaa63 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -451,14 +451,30 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.ObjectFilenameForDebug = OutputObjFile; } if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) { +#if LLVM_VERSION_GE(19, 0) + Options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib; +#else Options.CompressDebugSections = DebugCompressionType::Zlib; +#endif } else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) { +#if LLVM_VERSION_GE(19, 0) + Options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd; +#else Options.CompressDebugSections = DebugCompressionType::Zstd; +#endif } else if (!strcmp("none", DebugInfoCompression)) { +#if LLVM_VERSION_GE(19, 0) + Options.MCOptions.CompressDebugSections = DebugCompressionType::None; +#else Options.CompressDebugSections = DebugCompressionType::None; +#endif } +#if LLVM_VERSION_GE(19, 0) + Options.MCOptions.X86RelaxRelocations = RelaxELFRelocations; +#else Options.RelaxELFRelocations = RelaxELFRelocations; +#endif Options.UseInitArray = UseInitArray; #if LLVM_VERSION_LT(17, 0) From cf299ddb6e0f1b0ddc737805df49952d41e03754 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 6 Mar 2024 17:52:53 +0000 Subject: [PATCH 25/27] Make TAITs capture all higher-ranked lifetimes in scope --- compiler/rustc_ast_lowering/src/item.rs | 14 ++++- compiler/rustc_ast_lowering/src/lib.rs | 27 ++++----- .../src/region_infer/opaque_types.rs | 19 ++++--- compiler/rustc_hir/src/hir.rs | 2 + .../rustc_hir_analysis/src/check/check.rs | 21 +++---- .../src/collect/resolve_bound_vars.rs | 56 ++----------------- .../rustc_hir_analysis/src/collect/type_of.rs | 4 +- .../rustc_hir_analysis/src/variance/mod.rs | 10 +--- .../rustc_infer/src/infer/opaque_types/mod.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 12 ---- compiler/rustc_ty_utils/src/opaque_types.rs | 2 +- .../escaping-bound-var.rs | 2 +- .../escaping-bound-var.stderr | 12 +++- .../type-alias-impl-trait/self-referential.rs | 4 +- .../self-referential.stderr | 12 ++-- tests/ui/type-alias-impl-trait/variance.rs | 30 +++++----- .../ui/type-alias-impl-trait/variance.stderr | 45 +++++++++------ 17 files changed, 122 insertions(+), 152 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 01b1e6fcaff64..fbd35dd07ef8d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -275,7 +275,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } Some(ty) => this.lower_ty( ty, - ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty: false }, + ImplTraitContext::TypeAliasesOpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { + parent: this.local_def_id(id), + in_assoc_ty: false, + }, + }, ), }, ); @@ -936,7 +941,12 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(ty) => { let ty = this.lower_ty( ty, - ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty: true }, + ImplTraitContext::TypeAliasesOpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { + parent: this.local_def_id(i.id), + in_assoc_ty: true, + }, + }, ); hir::ImplItemKind::Type(ty) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 03ad75ca8b4a4..584f8b4691153 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -271,7 +271,10 @@ enum ImplTraitContext { fn_kind: FnDeclKind, }, /// Impl trait in type aliases. - TypeAliasesOpaqueTy { in_assoc_ty: bool }, + TypeAliasesOpaqueTy { + /// Origin: Always OpaqueTyOrigin::TypeAliasImplTrait + origin: hir::OpaqueTyOrigin, + }, /// `impl Trait` is unstably accepted in this position. FeatureGated(ImplTraitPosition, Symbol), /// `impl Trait` is not accepted in this position. @@ -1426,15 +1429,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Some(fn_kind), itctx, ), - ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty } => self - .lower_opaque_impl_trait( - span, - hir::OpaqueTyOrigin::TyAlias { in_assoc_ty }, - *def_node_id, - bounds, - None, - itctx, - ), + ImplTraitContext::TypeAliasesOpaqueTy { origin } => self + .lower_opaque_impl_trait(span, origin, *def_node_id, bounds, None, itctx), ImplTraitContext::Universal => { let span = t.span; @@ -1553,9 +1549,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let captured_lifetimes_to_duplicate = match origin { hir::OpaqueTyOrigin::TyAlias { .. } => { - // in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't duplicate any - // lifetimes, since we don't have the issue that any are late-bound. - Vec::new() + // type alias impl trait and associated type position impl trait were + // decided to capture all in-scope lifetimes, which we collect for + // all opaques during resolution. + self.resolver + .take_extra_lifetime_params(opaque_ty_node_id) + .into_iter() + .map(|(ident, id, _)| Lifetime { id, ident }) + .collect() } hir::OpaqueTyOrigin::FnReturn(..) => { if matches!( diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 4b096a592344a..12b02c7fcfa1d 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -367,14 +367,17 @@ fn check_opaque_type_parameter_valid( span: Span, ) -> Result<(), ErrorGuaranteed> { let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id); - let is_ty_alias = match opaque_ty_hir.expect_opaque_ty().origin { - OpaqueTyOrigin::TyAlias { .. } => true, - OpaqueTyOrigin::AsyncFn(..) | OpaqueTyOrigin::FnReturn(..) => false, + let (parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin { + OpaqueTyOrigin::TyAlias { parent, .. } => (parent, true), + OpaqueTyOrigin::AsyncFn(parent) | OpaqueTyOrigin::FnReturn(parent) => (parent, false), }; - let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let parent_generics = tcx.generics_of(parent); let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); - for (i, arg) in opaque_type_key.args.iter().enumerate() { + + // Only check the parent generics, which will ignore any of the + // duplicated lifetime args that come from reifying late-bounds. + for (i, arg) in opaque_type_key.args.iter().take(parent_generics.count()).enumerate() { if let Err(guar) = arg.error_reported() { return Err(guar); } @@ -395,7 +398,7 @@ fn check_opaque_type_parameter_valid( seen_params.entry(arg).or_default().push(i); } else { // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = opaque_generics.param_at(i, tcx); + let opaque_param = parent_generics.param_at(i, tcx); let kind = opaque_param.kind.descr(); return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam { @@ -409,10 +412,10 @@ fn check_opaque_type_parameter_valid( for (_, indices) in seen_params { if indices.len() > 1 { - let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); + let descr = parent_generics.param_at(indices[0], tcx).kind.descr(); let spans: Vec<_> = indices .into_iter() - .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) + .map(|i| tcx.def_span(parent_generics.param_at(i, tcx).def_id)) .collect(); #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3dac6880f17e1..180af3943b346 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2562,6 +2562,8 @@ pub enum OpaqueTyOrigin { AsyncFn(LocalDefId), /// type aliases: `type Foo = impl Trait;` TyAlias { + /// The type alias or associated type parent of the TAIT/ATPIT + parent: LocalDefId, /// associated types in impl blocks for traits. in_assoc_ty: bool, }, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 748571c12b3df..a5731a6c21e96 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -339,8 +339,9 @@ fn check_opaque_meets_bounds<'tcx>( origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { let defining_use_anchor = match *origin { - hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, - hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id), + hir::OpaqueTyOrigin::FnReturn(did) + | hir::OpaqueTyOrigin::AsyncFn(did) + | hir::OpaqueTyOrigin::TyAlias { parent: did, .. } => did, }; let param_env = tcx.param_env(defining_use_anchor); @@ -351,14 +352,14 @@ fn check_opaque_meets_bounds<'tcx>( let ocx = ObligationCtxt::new(&infcx); let args = match *origin { - hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => { - GenericArgs::identity_for_item(tcx, parent).extend_to( - tcx, - def_id.to_def_id(), - |param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(), - ) - } - hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id), + hir::OpaqueTyOrigin::FnReturn(parent) + | hir::OpaqueTyOrigin::AsyncFn(parent) + | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( + tcx, parent, + ) + .extend_to(tcx, def_id.to_def_id(), |param, _| { + tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into() + }), }; let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index ad8ec1036efee..607ada8b5edaa 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -514,38 +514,11 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(self, item); } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { .. }, - .. - }) => { - // Opaque types are visited when we visit the - // `TyKind::OpaqueDef`, so that they have the lifetimes from - // their parent opaque_ty in scope. - // - // The core idea here is that since OpaqueTys are generated with the impl Trait as - // their owner, we can keep going until we find the Item that owns that. We then - // conservatively add all resolved lifetimes. Otherwise we run into problems in - // cases like `type Foo<'a> = impl Bar`. - let parent_item = self.tcx.hir().get_parent_item(item.hir_id()); - let resolved_lifetimes: &ResolveBoundVars = - self.tcx.resolve_bound_vars(parent_item); - // We need to add *all* deps, since opaque tys may want them from *us* - for (&owner, defs) in resolved_lifetimes.defs.iter() { - defs.iter().for_each(|(&local_id, region)| { - self.map.defs.insert(hir::HirId { owner, local_id }, *region); - }); - } - for (&owner, late_bound_vars) in resolved_lifetimes.late_bound_vars.iter() { - late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { - self.record_late_bound_vars( - hir::HirId { owner, local_id }, - late_bound_vars.clone(), - ); - }); - } - } hir::ItemKind::OpaqueTy(&hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent), + origin: + hir::OpaqueTyOrigin::FnReturn(parent) + | hir::OpaqueTyOrigin::AsyncFn(parent) + | hir::OpaqueTyOrigin::TyAlias { parent, .. }, generics, .. }) => { @@ -683,26 +656,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // the opaque_ty generics let opaque_ty = self.tcx.hir().item(item_id); match &opaque_ty.kind { - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { .. }, - .. - }) => { - intravisit::walk_ty(self, ty); - - // Elided lifetimes and late-bound lifetimes (from the parent) - // are not allowed in non-return position impl Trait - let scope = Scope::LateBoundary { - s: &Scope::TraitRefBoundary { s: self.scope }, - what: "type alias impl trait", - }; - self.with(scope, |this| intravisit::walk_item(this, opaque_ty)); - - return; - } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), - .. - }) => {} + hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: _, .. }) => {} i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), }; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 2217e5280a736..3d54dd6f9f6b0 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -553,11 +553,11 @@ pub(super) fn type_of_opaque( Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false }, + origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. }, .. }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id), ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true }, + origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. }, .. }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id), // Opaque types desugared from `impl Trait`. diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 9618c6d8011ff..42885b0c83282 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -125,15 +125,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc // By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt // lifetime generics. - let variances = std::iter::repeat(ty::Invariant).take(generics.count()); - - let mut variances: Vec<_> = match tcx.opaque_type_origin(item_def_id) { - rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => { - variances.collect() - } - // But TAIT are invariant for all generics - rustc_hir::OpaqueTyOrigin::TyAlias { .. } => return tcx.arena.alloc_from_iter(variances), - }; + let mut variances = vec![ty::Invariant; generics.count()]; // Mark all lifetimes from parent generics as unused (Bivariant). // This will be overridden later if required. diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index b6f3c38cb3f01..3d6829ba6579f 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -391,7 +391,7 @@ impl<'tcx> InferCtxt<'tcx> { // Anonymous `impl Trait` hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, // Named `type Foo = impl Bar;` - hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { + hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => { if in_assoc_ty { self.tcx.opaque_types_defined_by(parent_def_id).contains(&def_id) } else { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4edec0e6951cd..5c2d3973d61dd 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1940,18 +1940,6 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait)) } - /// Returns the `DefId` of the item within which the `impl Trait` is declared. - /// For type-alias-impl-trait this is the `type` alias. - /// For impl-trait-in-assoc-type this is the assoc type. - /// For return-position-impl-trait this is the function. - pub fn impl_trait_parent(self, mut def_id: LocalDefId) -> LocalDefId { - // Find the surrounding item (type alias or assoc type) - while let DefKind::OpaqueTy = self.def_kind(def_id) { - def_id = self.local_parent(def_id); - } - def_id - } - pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { if self.def_kind(def_id) != DefKind::AssocFn { return false; diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index c6448b4f661d7..3dcc8382289f5 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -141,7 +141,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { trace!(?origin); match origin { rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} - rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { + rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => { if !in_assoc_ty { if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { return; diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs index bf27e76db2b4d..b030ce39d1dfc 100644 --- a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs +++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs @@ -7,7 +7,7 @@ pub trait Trait<'a> { trait Test<'a> {} pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>; -//~^ ERROR cannot capture late-bound lifetime in type alias impl trait +//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet impl Trait<'_> for () { type Assoc = (); diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr b/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr index 7dce067d39c68..706de37e9f3c7 100644 --- a/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr +++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr @@ -1,8 +1,14 @@ -error: cannot capture late-bound lifetime in type alias impl trait - --> $DIR/escaping-bound-var.rs:9:57 +error: higher kinded lifetime bounds on nested opaque types are not supported yet + --> $DIR/escaping-bound-var.rs:9:25 | LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>; - | -- lifetime defined here ^^ + | ^^ + | +note: lifetime declared here + --> $DIR/escaping-bound-var.rs:9:25 + | +LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>; + | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/self-referential.rs b/tests/ui/type-alias-impl-trait/self-referential.rs index 3090f7733d24d..2bd450e6c8609 100644 --- a/tests/ui/type-alias-impl-trait/self-referential.rs +++ b/tests/ui/type-alias-impl-trait/self-referential.rs @@ -12,14 +12,14 @@ fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { type Foo<'a, 'b> = (i32, impl PartialEq> + std::fmt::Debug); fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { - //~^ ERROR can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0})` + //~^ ERROR can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` (42, i) } type Moo<'a, 'b> = (i32, impl PartialEq> + std::fmt::Debug); fn moo<'a, 'b>(i: &'a i32) -> Moo<'a, 'b> { - //~^ ERROR can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0})` + //~^ ERROR can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` (42, i) } diff --git a/tests/ui/type-alias-impl-trait/self-referential.stderr b/tests/ui/type-alias-impl-trait/self-referential.stderr index 27506b8d06f80..396237c78980c 100644 --- a/tests/ui/type-alias-impl-trait/self-referential.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential.stderr @@ -10,28 +10,28 @@ LL | i = help: the trait `PartialEq>` is not implemented for `&i32` = help: the trait `PartialEq` is implemented for `i32` -error[E0277]: can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0})` +error[E0277]: can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` --> $DIR/self-referential.rs:14:31 | LL | fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { - | ^^^^^^^^^^^ no implementation for `&i32 == (i32, Foo<'a, 'b>::{opaque#0})` + | ^^^^^^^^^^^ no implementation for `&i32 == (i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` LL | LL | (42, i) | ------- return type was inferred to be `(i32, &i32)` here | - = help: the trait `PartialEq<(i32, Foo<'a, 'b>::{opaque#0})>` is not implemented for `&i32` + = help: the trait `PartialEq<(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)>` is not implemented for `&i32` = help: the trait `PartialEq` is implemented for `i32` -error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0})` +error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` --> $DIR/self-referential.rs:21:31 | LL | fn moo<'a, 'b>(i: &'a i32) -> Moo<'a, 'b> { - | ^^^^^^^^^^^ no implementation for `&i32 == (i32, Moo<'b, 'a>::{opaque#0})` + | ^^^^^^^^^^^ no implementation for `&i32 == (i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` LL | LL | (42, i) | ------- return type was inferred to be `(i32, &i32)` here | - = help: the trait `PartialEq<(i32, Moo<'b, 'a>::{opaque#0})>` is not implemented for `&i32` + = help: the trait `PartialEq<(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)>` is not implemented for `&i32` = help: the trait `PartialEq` is implemented for `i32` error: aborting due to 3 previous errors diff --git a/tests/ui/type-alias-impl-trait/variance.rs b/tests/ui/type-alias-impl-trait/variance.rs index eae5e5fdde2b8..457c4affb9d22 100644 --- a/tests/ui/type-alias-impl-trait/variance.rs +++ b/tests/ui/type-alias-impl-trait/variance.rs @@ -5,21 +5,21 @@ trait Captures<'a> {} impl Captures<'_> for T {} -type NotCapturedEarly<'a> = impl Sized; //~ [o] +type NotCapturedEarly<'a> = impl Sized; //~ [*, o] //~^ ERROR: unconstrained opaque type -type CapturedEarly<'a> = impl Sized + Captures<'a>; //~ [o] +type CapturedEarly<'a> = impl Sized + Captures<'a>; //~ [*, o] //~^ ERROR: unconstrained opaque type -// TAIT does *not* capture `'b` -type NotCapturedLate<'a> = dyn for<'b> Iterator; //~ [o] -//~^ ERROR: unconstrained opaque type +type NotCapturedLate<'a> = dyn for<'b> Iterator; //~ [*, o, o] +//~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level +//~| ERROR: unconstrained opaque type -// TAIT does *not* capture `'b` -type Captured<'a> = dyn for<'b> Iterator>; //~ [o] -//~^ ERROR: unconstrained opaque type +type Captured<'a> = dyn for<'b> Iterator>; //~ [*, o, o] +//~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level +//~| ERROR: unconstrained opaque type -type Bar<'a, 'b: 'b, T> = impl Sized; //~ ERROR [o, o, o] +type Bar<'a, 'b: 'b, T> = impl Sized; //~ ERROR [*, *, o, o, o] //~^ ERROR: unconstrained opaque type trait Foo<'i> { @@ -31,24 +31,24 @@ trait Foo<'i> { } impl<'i> Foo<'i> for &'i () { - type ImplicitCapture<'a> = impl Sized; //~ [o, o] + type ImplicitCapture<'a> = impl Sized; //~ [*, *, o, o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ [o, o] + type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ [*, *, o, o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ [o, o] + type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ [*, *, o, o] //~^ ERROR: unconstrained opaque type } impl<'i> Foo<'i> for () { - type ImplicitCapture<'a> = impl Sized; //~ [o, o] + type ImplicitCapture<'a> = impl Sized; //~ [*, *, o, o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ [o, o] + type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ [*, *, o, o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ [o, o] + type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ [*, *, o, o] //~^ ERROR: unconstrained opaque type } diff --git a/tests/ui/type-alias-impl-trait/variance.stderr b/tests/ui/type-alias-impl-trait/variance.stderr index 914541fcf66ee..eafe583e89c26 100644 --- a/tests/ui/type-alias-impl-trait/variance.stderr +++ b/tests/ui/type-alias-impl-trait/variance.stderr @@ -1,3 +1,15 @@ +error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level + --> $DIR/variance.rs:14:36 + | +LL | type NotCapturedLate<'a> = dyn for<'b> Iterator; + | ^^ + +error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level + --> $DIR/variance.rs:18:29 + | +LL | type Captured<'a> = dyn for<'b> Iterator>; + | ^^ + error: unconstrained opaque type --> $DIR/variance.rs:8:29 | @@ -15,7 +27,7 @@ LL | type CapturedEarly<'a> = impl Sized + Captures<'a>; = note: `CapturedEarly` must be used in combination with a concrete type within the same module error: unconstrained opaque type - --> $DIR/variance.rs:15:56 + --> $DIR/variance.rs:14:56 | LL | type NotCapturedLate<'a> = dyn for<'b> Iterator; | ^^^^^^^^^^ @@ -23,7 +35,7 @@ LL | type NotCapturedLate<'a> = dyn for<'b> Iterator; = note: `NotCapturedLate` must be used in combination with a concrete type within the same module error: unconstrained opaque type - --> $DIR/variance.rs:19:49 + --> $DIR/variance.rs:18:49 | LL | type Captured<'a> = dyn for<'b> Iterator>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -86,71 +98,72 @@ LL | type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; | = note: `ExplicitCaptureFromGat` must be used in combination with a concrete type within the same impl -error: [o] +error: [*, o] --> $DIR/variance.rs:8:29 | LL | type NotCapturedEarly<'a> = impl Sized; | ^^^^^^^^^^ -error: [o] +error: [*, o] --> $DIR/variance.rs:11:26 | LL | type CapturedEarly<'a> = impl Sized + Captures<'a>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: [o] - --> $DIR/variance.rs:15:56 +error: [*, o, o] + --> $DIR/variance.rs:14:56 | LL | type NotCapturedLate<'a> = dyn for<'b> Iterator; | ^^^^^^^^^^ -error: [o] - --> $DIR/variance.rs:19:49 +error: [*, o, o] + --> $DIR/variance.rs:18:49 | LL | type Captured<'a> = dyn for<'b> Iterator>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: [o, o, o] +error: [*, *, o, o, o] --> $DIR/variance.rs:22:27 | LL | type Bar<'a, 'b: 'b, T> = impl Sized; | ^^^^^^^^^^ -error: [o, o] +error: [*, *, o, o] --> $DIR/variance.rs:34:32 | LL | type ImplicitCapture<'a> = impl Sized; | ^^^^^^^^^^ -error: [o, o] +error: [*, *, o, o] --> $DIR/variance.rs:37:42 | LL | type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: [o, o] +error: [*, *, o, o] --> $DIR/variance.rs:40:39 | LL | type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: [o, o] +error: [*, *, o, o] --> $DIR/variance.rs:45:32 | LL | type ImplicitCapture<'a> = impl Sized; | ^^^^^^^^^^ -error: [o, o] +error: [*, *, o, o] --> $DIR/variance.rs:48:42 | LL | type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: [o, o] +error: [*, *, o, o] --> $DIR/variance.rs:51:39 | LL | type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors +For more information about this error, try `rustc --explain E0657`. From 99df5a2b425c12ece7fd241f410719f7dfd2da97 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 6 Mar 2024 18:44:55 +0000 Subject: [PATCH 26/27] Simplify ImplTraitContext --- compiler/rustc_ast_lowering/src/item.rs | 6 ++-- compiler/rustc_ast_lowering/src/lib.rs | 43 ++++++++++--------------- compiler/rustc_ast_lowering/src/path.rs | 2 +- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index fbd35dd07ef8d..523001381864d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -275,11 +275,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } Some(ty) => this.lower_ty( ty, - ImplTraitContext::TypeAliasesOpaqueTy { + ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { parent: this.local_def_id(id), in_assoc_ty: false, }, + fn_kind: None, }, ), }, @@ -941,11 +942,12 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(ty) => { let ty = this.lower_ty( ty, - ImplTraitContext::TypeAliasesOpaqueTy { + ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { parent: this.local_def_id(i.id), in_assoc_ty: true, }, + fn_kind: None, }, ); hir::ImplItemKind::Type(ty) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 584f8b4691153..94e1e06a95453 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -265,15 +265,11 @@ enum ImplTraitContext { /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// - ReturnPositionOpaqueTy { - /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn, - origin: hir::OpaqueTyOrigin, - fn_kind: FnDeclKind, - }, - /// Impl trait in type aliases. - TypeAliasesOpaqueTy { - /// Origin: Always OpaqueTyOrigin::TypeAliasImplTrait + OpaqueTy { origin: hir::OpaqueTyOrigin, + /// Only used to change the lifetime capture rules, since + /// RPITIT captures all in scope, RPIT does not. + fn_kind: Option, }, /// `impl Trait` is unstably accepted in this position. FeatureGated(ImplTraitPosition, Symbol), @@ -1078,9 +1074,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Disallow ATB in dyn types if self.is_in_dyn_type { let suggestion = match itctx { - ImplTraitContext::ReturnPositionOpaqueTy { .. } - | ImplTraitContext::TypeAliasesOpaqueTy { .. } - | ImplTraitContext::Universal => { + ImplTraitContext::OpaqueTy { .. } | ImplTraitContext::Universal => { let bound_end_span = constraint .gen_args .as_ref() @@ -1420,17 +1414,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; match itctx { - ImplTraitContext::ReturnPositionOpaqueTy { origin, fn_kind } => self - .lower_opaque_impl_trait( - span, - origin, - *def_node_id, - bounds, - Some(fn_kind), - itctx, - ), - ImplTraitContext::TypeAliasesOpaqueTy { origin } => self - .lower_opaque_impl_trait(span, origin, *def_node_id, bounds, None, itctx), + ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait( + span, + origin, + *def_node_id, + bounds, + fn_kind, + itctx, + ), ImplTraitContext::Universal => { let span = t.span; @@ -1824,9 +1815,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Trait - | FnDeclKind::Impl => ImplTraitContext::ReturnPositionOpaqueTy { + | FnDeclKind::Impl => ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)), - fn_kind: kind, + fn_kind: Some(kind), }, FnDeclKind::ExternFn => { ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn) @@ -1920,9 +1911,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { output, coro, opaque_ty_span, - ImplTraitContext::ReturnPositionOpaqueTy { + ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), - fn_kind, + fn_kind: Some(fn_kind), }, ); arena_vec![this; bound] diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 48941a232c266..aeeb4bf9e763a 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -423,7 +423,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // fn f(_: impl Fn() -> impl Debug) -> impl Fn() -> impl Debug // // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^ // ``` - FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::ReturnPositionOpaqueTy { .. }) => { + FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => { if self.tcx.features().impl_trait_in_fn_trait_return { self.lower_ty(ty, itctx) } else { From 74d5bbbf94418d87def5acd89343b63d36ada5e0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 6 Mar 2024 18:48:13 +0000 Subject: [PATCH 27/27] Rename some functions to represent their generalized behavior --- .../rustc_hir_analysis/src/check/check.rs | 2 +- .../src/collect/predicates_of.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 20 +++++++++---------- compiler/rustc_ty_utils/src/implied_bounds.rs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index a5731a6c21e96..1b24c2b61fd07 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -358,7 +358,7 @@ fn check_opaque_meets_bounds<'tcx>( tcx, parent, ) .extend_to(tcx, def_id.to_def_id(), |param, _| { - tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into() + tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() }), }; diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index f70bb8c4289f6..2675eacc06e19 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -339,7 +339,7 @@ fn compute_bidirectional_outlives_predicates<'tcx>( predicates: &mut Vec<(ty::Clause<'tcx>, Span)>, ) { for param in opaque_own_params { - let orig_lifetime = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); + let orig_lifetime = tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()); if let ty::ReEarlyParam(..) = *orig_lifetime { let dup_lifetime = ty::Region::new_early_param( tcx, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 4f8e4aa90f403..4a04e6771805c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1254,7 +1254,7 @@ impl<'tcx> TyCtxt<'tcx> { if self.def_kind(scope) == DefKind::OpaqueTy { // Lifetime params of opaque types are synthetic and thus irrelevant to // diagnostics. Map them back to their origin! - region = self.map_rpit_lifetime_to_fn_lifetime(def_id); + region = self.map_opaque_lifetime_to_parent_lifetime(def_id); continue; } break (scope, ty::BrNamed(def_id.into(), self.item_name(def_id.into()))); @@ -2219,31 +2219,31 @@ impl<'tcx> TyCtxt<'tcx> { ) } - /// Given the def-id of an early-bound lifetime on an RPIT corresponding to + /// Given the def-id of an early-bound lifetime on an opaque corresponding to /// a duplicated captured lifetime, map it back to the early- or late-bound /// lifetime of the function from which it originally as captured. If it is /// a late-bound lifetime, this will represent the liberated (`ReLateParam`) lifetime /// of the signature. // FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just // re-use the generics of the opaque, this function will need to be tweaked slightly. - pub fn map_rpit_lifetime_to_fn_lifetime( + pub fn map_opaque_lifetime_to_parent_lifetime( self, - mut rpit_lifetime_param_def_id: LocalDefId, + mut opaque_lifetime_param_def_id: LocalDefId, ) -> ty::Region<'tcx> { debug_assert!( - matches!(self.def_kind(rpit_lifetime_param_def_id), DefKind::LifetimeParam), - "{rpit_lifetime_param_def_id:?} is a {}", - self.def_descr(rpit_lifetime_param_def_id.to_def_id()) + matches!(self.def_kind(opaque_lifetime_param_def_id), DefKind::LifetimeParam), + "{opaque_lifetime_param_def_id:?} is a {}", + self.def_descr(opaque_lifetime_param_def_id.to_def_id()) ); loop { - let parent = self.local_parent(rpit_lifetime_param_def_id); + let parent = self.local_parent(opaque_lifetime_param_def_id); let hir::OpaqueTy { lifetime_mapping, .. } = self.hir_node_by_def_id(parent).expect_item().expect_opaque_ty(); let Some((lifetime, _)) = lifetime_mapping .iter() - .find(|(_, duplicated_param)| *duplicated_param == rpit_lifetime_param_def_id) + .find(|(_, duplicated_param)| *duplicated_param == opaque_lifetime_param_def_id) else { bug!("duplicated lifetime param should be present"); }; @@ -2256,7 +2256,7 @@ impl<'tcx> TyCtxt<'tcx> { // of the opaque we mapped from. Continue mapping. if matches!(self.def_kind(new_parent), DefKind::OpaqueTy) { debug_assert_eq!(self.parent(parent.to_def_id()), new_parent); - rpit_lifetime_param_def_id = ebv.expect_local(); + opaque_lifetime_param_def_id = ebv.expect_local(); continue; } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 191671bcc1edd..87462963c27c5 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -71,7 +71,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // the end of the list corresponding to the opaque's generics. for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] { let orig_lt = - tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); + tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()); if matches!(*orig_lt, ty::ReLateParam(..)) { mapping.insert( orig_lt,