From 6d455296fde6ff0d5154d39513259a60eb73f302 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 25 Oct 2022 20:47:19 +0800 Subject: [PATCH 01/22] Fix #103451, find_width_of_character_at_span return width with 1 when reaching end --- compiler/rustc_span/src/source_map.rs | 8 ++---- compiler/rustc_span/src/source_map/tests.rs | 11 +++---- src/test/ui/parser/issue-103451.rs | 5 ++++ src/test/ui/parser/issue-103451.stderr | 32 +++++++++++++++++++++ 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/parser/issue-103451.rs create mode 100644 src/test/ui/parser/issue-103451.stderr diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index f9566eeee9465..3baa2e03cbad7 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -855,7 +855,8 @@ impl SourceMap { /// Returns a new span representing the next character after the end-point of this span. /// Special cases: /// - if span is a dummy one, returns the same span - /// - if next_point reached the end of source, return span with lo = hi + /// - if next_point reached the end of source, return a span exceeding the end of source, + /// which means sm.span_to_snippet(next_point) will get `Err` /// - respect multi-byte characters pub fn next_point(&self, sp: Span) -> Span { if sp.is_dummy() { @@ -864,9 +865,6 @@ impl SourceMap { let start_of_next_point = sp.hi().0; let width = self.find_width_of_character_at_span(sp, true); - if width == 0 { - return Span::new(sp.hi(), sp.hi(), sp.ctxt(), None); - } // If the width is 1, then the next span should only contain the next char besides current ending. // However, in the case of a multibyte character, where the width != 1, the next span should // span multiple bytes to include the whole character. @@ -938,7 +936,7 @@ impl SourceMap { // Ensure indexes are also not malformed. if start_index > end_index || end_index > source_len - 1 { debug!("find_width_of_character_at_span: source indexes are malformed"); - return 0; + return 1; } let src = local_begin.sf.external_src.borrow(); diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 1fd81018fa05c..3cab59e8dbe6c 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -511,16 +511,17 @@ fn test_next_point() { assert_eq!(span.lo().0, 4); assert_eq!(span.hi().0, 5); - // A non-empty span at the last byte should advance to create an empty - // span pointing at the end of the file. + // Reaching to the end of file, return a span that will get error with `span_to_snippet` let span = Span::with_root_ctxt(BytePos(4), BytePos(5)); let span = sm.next_point(span); assert_eq!(span.lo().0, 5); - assert_eq!(span.hi().0, 5); + assert_eq!(span.hi().0, 6); + assert!(sm.span_to_snippet(span).is_err()); - // Empty span pointing just past the last byte. + // Reaching to the end of file, return a span that will get error with `span_to_snippet` let span = Span::with_root_ctxt(BytePos(5), BytePos(5)); let span = sm.next_point(span); assert_eq!(span.lo().0, 5); - assert_eq!(span.hi().0, 5); + assert_eq!(span.hi().0, 6); + assert!(sm.span_to_snippet(span).is_err()); } diff --git a/src/test/ui/parser/issue-103451.rs b/src/test/ui/parser/issue-103451.rs new file mode 100644 index 0000000000000..1fdb001488155 --- /dev/null +++ b/src/test/ui/parser/issue-103451.rs @@ -0,0 +1,5 @@ +// error-pattern: this file contains an unclosed delimiter +// error-pattern: expected value, found struct `R` +struct R { } +struct S { + x: [u8; R diff --git a/src/test/ui/parser/issue-103451.stderr b/src/test/ui/parser/issue-103451.stderr new file mode 100644 index 0000000000000..eb3c92fb43d9b --- /dev/null +++ b/src/test/ui/parser/issue-103451.stderr @@ -0,0 +1,32 @@ +error: this file contains an unclosed delimiter + --> $DIR/issue-103451.rs:5:15 + | +LL | struct S { + | - unclosed delimiter +LL | x: [u8; R + | - ^ + | | + | unclosed delimiter + +error: this file contains an unclosed delimiter + --> $DIR/issue-103451.rs:5:15 + | +LL | struct S { + | - unclosed delimiter +LL | x: [u8; R + | - ^ + | | + | unclosed delimiter + +error[E0423]: expected value, found struct `R` + --> $DIR/issue-103451.rs:5:13 + | +LL | struct R { } + | ------------ `R` defined here +LL | struct S { +LL | x: [u8; R + | ^ help: use struct literal syntax instead: `R {}` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0423`. From a2f3649152f917b95c6c6d017aefa85ab15ec4c6 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Wed, 3 Aug 2022 04:30:13 -0700 Subject: [PATCH 02/22] Add support for custom MIR parsing --- compiler/rustc_borrowck/src/lib.rs | 14 + compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_feature/src/builtin_attrs.rs | 4 + compiler/rustc_middle/src/mir/mod.rs | 59 +++++ .../rustc_mir_build/src/build/custom/mod.rs | 155 +++++++++++ .../rustc_mir_build/src/build/custom/parse.rs | 245 ++++++++++++++++++ .../src/build/custom/parse/instruction.rs | 72 +++++ compiler/rustc_mir_build/src/build/mod.rs | 17 ++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 16 +- compiler/rustc_mir_build/src/thir/cx/mod.rs | 11 +- .../rustc_mir_transform/src/check_unsafety.rs | 8 + .../rustc_mir_transform/src/pass_manager.rs | 73 +++--- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics.rs | 3 + library/core/src/intrinsics/mir.rs | 123 +++++++++ .../custom/references.immut_ref.mir_map.0.mir | 14 + .../custom/references.mut_ref.mir_map.0.mir | 14 + .../mir-opt/building/custom/references.rs | 43 +++ .../mir-opt/building/custom/simple_assign.rs | 37 +++ .../custom/simple_assign.simple.mir_map.0.mir | 18 ++ .../simple_assign.simple_ref.mir_map.0.mir | 10 + .../feature-gates/feature-gate-custom_mir.rs | 12 + .../feature-gate-custom_mir.stderr | 11 + 23 files changed, 921 insertions(+), 41 deletions(-) create mode 100644 compiler/rustc_mir_build/src/build/custom/mod.rs create mode 100644 compiler/rustc_mir_build/src/build/custom/parse.rs create mode 100644 compiler/rustc_mir_build/src/build/custom/parse/instruction.rs create mode 100644 library/core/src/intrinsics/mir.rs create mode 100644 src/test/mir-opt/building/custom/references.immut_ref.mir_map.0.mir create mode 100644 src/test/mir-opt/building/custom/references.mut_ref.mir_map.0.mir create mode 100644 src/test/mir-opt/building/custom/references.rs create mode 100644 src/test/mir-opt/building/custom/simple_assign.rs create mode 100644 src/test/mir-opt/building/custom/simple_assign.simple.mir_map.0.mir create mode 100644 src/test/mir-opt/building/custom/simple_assign.simple_ref.mir_map.0.mir create mode 100644 src/test/ui/feature-gates/feature-gate-custom_mir.rs create mode 100644 src/test/ui/feature-gates/feature-gate-custom_mir.stderr diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index abfe253d43df7..4a4887f19702f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -18,6 +18,7 @@ extern crate tracing; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::vec_map::VecMap; use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -129,6 +130,19 @@ fn mir_borrowck<'tcx>( ) -> &'tcx BorrowCheckResult<'tcx> { let (input_body, promoted) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); + + if input_body.borrow().should_skip() { + debug!("Skipping borrowck because of injected body"); + // Let's make up a borrowck result! Fun times! + let result = BorrowCheckResult { + concrete_opaque_types: VecMap::new(), + closure_requirements: None, + used_mut_upvars: SmallVec::new(), + tainted_by_errors: None, + }; + return tcx.arena.alloc(result); + } + let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner; let infcx = diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 4facb6140a300..c0ff8436a69c5 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -152,6 +152,8 @@ declare_features! ( (active, anonymous_lifetime_in_impl_trait, "1.63.0", None, None), /// Allows identifying the `compiler_builtins` crate. (active, compiler_builtins, "1.13.0", None, None), + /// Allows writing custom MIR + (active, custom_mir, "1.65.0", None, None), /// Outputs useful `assert!` messages (active, generic_assert, "1.63.0", None, None), /// Allows using the `rust-intrinsic`'s "ABI". diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 2ead3c2c8d485..2bc28d20d9f10 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -818,6 +818,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), + gated!( + custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#), + ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite", + ), rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 3b80af184be47..ccbe30c268fd0 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -138,6 +138,48 @@ impl MirPhase { } } } + + /// Parses an `MirPhase` from a pair of strings. Panics if this isn't possible for any reason. + pub fn parse(dialect: String, phase: Option) -> Self { + match &*dialect.to_ascii_lowercase() { + "built" => { + assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); + MirPhase::Built + } + "analysis" => Self::Analysis(AnalysisPhase::parse(phase)), + "runtime" => Self::Runtime(RuntimePhase::parse(phase)), + _ => panic!("Unknown MIR dialect {}", dialect), + } + } +} + +impl AnalysisPhase { + pub fn parse(phase: Option) -> Self { + let Some(phase) = phase else { + return Self::Initial; + }; + + match &*phase.to_ascii_lowercase() { + "initial" => Self::Initial, + "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, + _ => panic!("Unknown analysis phase {}", phase), + } + } +} + +impl RuntimePhase { + pub fn parse(phase: Option) -> Self { + let Some(phase) = phase else { + return Self::Initial; + }; + + match &*phase.to_ascii_lowercase() { + "initial" => Self::Initial, + "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, + "optimized" => Self::Optimized, + _ => panic!("Unknown runtime phase {}", phase), + } + } } impl Display for MirPhase { @@ -293,6 +335,13 @@ pub struct Body<'tcx> { /// potentially allow things like `[u8; std::mem::size_of::() * 0]` due to this. pub is_polymorphic: bool, + /// The phase at which this MIR should be "injected" into the compilation process. + /// + /// Everything that comes before this `MirPhase` should be skipped. + /// + /// This is only `Some` if the function that this body comes from was annotated with `rustc_custom_mir`. + pub injection_phase: Option, + pub tainted_by_errors: Option, } @@ -339,6 +388,7 @@ impl<'tcx> Body<'tcx> { span, required_consts: Vec::new(), is_polymorphic: false, + injection_phase: None, tainted_by_errors, }; body.is_polymorphic = body.has_non_region_param(); @@ -366,6 +416,7 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, + injection_phase: None, tainted_by_errors: None, }; body.is_polymorphic = body.has_non_region_param(); @@ -508,6 +559,14 @@ impl<'tcx> Body<'tcx> { pub fn generator_kind(&self) -> Option { self.generator.as_ref().map(|generator| generator.generator_kind) } + + #[inline] + pub fn should_skip(&self) -> bool { + let Some(injection_phase) = self.injection_phase else { + return false; + }; + injection_phase > self.phase + } } #[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs new file mode 100644 index 0000000000000..68d8766c90734 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -0,0 +1,155 @@ +//! Provides the implementation of the `custom_mir` attribute. +//! +//! Up until MIR building, this attribute has absolutely no effect. The `mir!` macro is a normal +//! decl macro that expands like any other, and the code goes through parsing, name resolution and +//! type checking like all other code. In MIR building we finally detect whether this attribute is +//! present, and if so we branch off into this module, which implements the attribute by +//! implementing a custom lowering from THIR to MIR. +//! +//! The result of this lowering is returned "normally" from the `mir_built` query, with the only +//! notable difference being that the `injected` field in the body is set. Various components of the +//! MIR pipeline, like borrowck and the pass manager will then consult this field (via +//! `body.should_skip()`) to skip the parts of the MIR pipeline that precede the MIR phase the user +//! specified. +//! +//! This file defines the general framework for the custom parsing. The parsing for all the +//! "top-level" constructs can be found in the `parse` submodule, while the parsing for statements, +//! terminators, and everything below can be found in the `parse::instruction` submodule. +//! + +use rustc_ast::Attribute; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_index::vec::IndexVec; +use rustc_middle::{ + mir::*, + thir::*, + ty::{Ty, TyCtxt}, +}; +use rustc_span::Span; + +mod parse; + +pub(super) fn build_custom_mir<'tcx>( + tcx: TyCtxt<'tcx>, + did: DefId, + thir: &Thir<'tcx>, + expr: ExprId, + params: &IndexVec>, + return_ty: Ty<'tcx>, + return_ty_span: Span, + span: Span, + attr: &Attribute, +) -> Body<'tcx> { + let mut body = Body { + basic_blocks: BasicBlocks::new(IndexVec::new()), + source: MirSource::item(did), + phase: MirPhase::Built, + source_scopes: IndexVec::new(), + generator: None, + local_decls: LocalDecls::new(), + user_type_annotations: IndexVec::new(), + arg_count: params.len(), + spread_arg: None, + var_debug_info: Vec::new(), + span, + required_consts: Vec::new(), + is_polymorphic: false, + tainted_by_errors: None, + injection_phase: None, + pass_count: 1, + }; + + body.local_decls.push(LocalDecl::new(return_ty, return_ty_span)); + body.basic_blocks_mut().push(BasicBlockData::new(None)); + body.source_scopes.push(SourceScopeData { + span, + parent_scope: None, + inlined: None, + inlined_parent_scope: None, + local_data: ClearCrossCrate::Clear, + }); + body.injection_phase = Some(parse_attribute(attr)); + + let mut pctxt = ParseCtxt { + tcx, + thir, + source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }, + body: &mut body, + local_map: FxHashMap::default(), + block_map: FxHashMap::default(), + }; + + let res = (|| { + pctxt.parse_args(¶ms)?; + pctxt.parse_body(expr) + })(); + if let Err(err) = res { + tcx.sess.diagnostic().span_fatal( + err.span, + format!("Could not parse {}, found: {:?}", err.expected, err.item_description), + ) + } + + body +} + +fn parse_attribute(attr: &Attribute) -> MirPhase { + let meta_items = attr.meta_item_list().unwrap(); + let mut dialect: Option = None; + let mut phase: Option = None; + + for nested in meta_items { + let name = nested.name_or_empty(); + let value = nested.value_str().unwrap().as_str().to_string(); + match name.as_str() { + "dialect" => { + assert!(dialect.is_none()); + dialect = Some(value); + } + "phase" => { + assert!(phase.is_none()); + phase = Some(value); + } + other => { + panic!("Unexpected key {}", other); + } + } + } + + let Some(dialect) = dialect else { + assert!(phase.is_none()); + return MirPhase::Built; + }; + + MirPhase::parse(dialect, phase) +} + +struct ParseCtxt<'tcx, 'body> { + tcx: TyCtxt<'tcx>, + thir: &'body Thir<'tcx>, + source_info: SourceInfo, + + body: &'body mut Body<'tcx>, + local_map: FxHashMap, + block_map: FxHashMap, +} + +struct ParseError { + span: Span, + item_description: String, + expected: String, +} + +impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { + fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError { + let expr = &self.thir[expr]; + ParseError { + span: expr.span, + item_description: format!("{:?}", expr.kind), + expected: expected.to_string(), + } + } +} + +type PResult = Result; diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs new file mode 100644 index 0000000000000..52cb0a4826d07 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/custom/parse.rs @@ -0,0 +1,245 @@ +use rustc_index::vec::IndexVec; +use rustc_middle::{mir::*, thir::*, ty::Ty}; +use rustc_span::Span; + +use super::{PResult, ParseCtxt, ParseError}; + +mod instruction; + +/// Helper macro for parsing custom MIR. +/// +/// Example usage looks something like: +/// ```rust,ignore (incomplete example) +/// parse_by_kind!( +/// self, // : &ParseCtxt +/// expr_id, // what you're matching against +/// "assignment", // the thing you're trying to parse +/// @call("mir_assign", args) => { args[0] }, // match invocations of the `mir_assign` special function +/// ExprKind::Assign { lhs, .. } => { lhs }, // match thir assignment expressions +/// // no need for fallthrough case - reasonable error is automatically generated +/// ) +/// ``` +macro_rules! parse_by_kind { + ( + $self:ident, + $expr_id:expr, + $expected:literal, + $( + @call($name:literal, $args:ident) => $call_expr:expr, + )* + $( + $pat:pat => $expr:expr, + )* + ) => {{ + let expr_id = $self.preparse($expr_id); + let expr = &$self.thir[expr_id]; + match &expr.kind { + $( + ExprKind::Call { ty, fun: _, args: $args, .. } if { + match ty.kind() { + ty::FnDef(did, _) => { + $self.tcx.is_diagnostic_item(rustc_span::Symbol::intern($name), *did) + } + _ => false, + } + } => $call_expr, + )* + $( + $pat => $expr, + )* + #[allow(unreachable_patterns)] + _ => return Err($self.expr_error(expr_id, $expected)) + } + }}; +} +pub(crate) use parse_by_kind; + +impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { + /// Expressions should only ever be matched on after preparsing them. This removes extra scopes + /// we don't care about. + fn preparse(&self, expr_id: ExprId) -> ExprId { + let expr = &self.thir[expr_id]; + match expr.kind { + ExprKind::Scope { value, .. } => self.preparse(value), + _ => expr_id, + } + } + + fn statement_as_expr(&self, stmt_id: StmtId) -> PResult { + match &self.thir[stmt_id].kind { + StmtKind::Expr { expr, .. } => Ok(*expr), + kind @ StmtKind::Let { pattern, .. } => { + return Err(ParseError { + span: pattern.span, + item_description: format!("{:?}", kind), + expected: "expression".to_string(), + }); + } + } + } + + pub fn parse_args(&mut self, params: &IndexVec>) -> PResult<()> { + for param in params.iter() { + let (var, span) = { + let pat = param.pat.as_ref().unwrap(); + match &pat.kind { + PatKind::Binding { var, .. } => (*var, pat.span), + _ => { + return Err(ParseError { + span: pat.span, + item_description: format!("{:?}", pat.kind), + expected: "local".to_string(), + }); + } + } + }; + let decl = LocalDecl::new(param.ty, span); + let local = self.body.local_decls.push(decl); + self.local_map.insert(var, local); + } + + Ok(()) + } + + /// Bodies are of the form: + /// + /// ```text + /// { + /// let bb1: BasicBlock; + /// let bb2: BasicBlock; + /// { + /// let RET: _; + /// let local1; + /// let local2; + /// + /// { + /// { // entry block + /// statement1; + /// terminator1 + /// }; + /// + /// bb1 = { + /// statement2; + /// terminator2 + /// }; + /// + /// bb2 = { + /// statement3; + /// terminator3 + /// } + /// + /// RET + /// } + /// } + /// } + /// ``` + /// + /// This allows us to easily parse the basic blocks declarations, local declarations, and + /// basic block definitions in order. + pub fn parse_body(&mut self, expr_id: ExprId) -> PResult<()> { + let body = parse_by_kind!(self, expr_id, "whole body", + ExprKind::Block { block } => self.thir[*block].expr.unwrap(), + ); + let (block_decls, rest) = parse_by_kind!(self, body, "body with block decls", + ExprKind::Block { block } => { + let block = &self.thir[*block]; + (&block.stmts, block.expr.unwrap()) + }, + ); + self.parse_block_decls(block_decls.iter().copied())?; + + let (local_decls, rest) = parse_by_kind!(self, rest, "body with local decls", + ExprKind::Block { block } => { + let block = &self.thir[*block]; + (&block.stmts, block.expr.unwrap()) + }, + ); + self.parse_local_decls(local_decls.iter().copied())?; + + let block_defs = parse_by_kind!(self, rest, "body with block defs", + ExprKind::Block { block } => &self.thir[*block].stmts, + ); + for (i, block_def) in block_defs.iter().enumerate() { + let block = self.parse_block_def(self.statement_as_expr(*block_def)?)?; + self.body.basic_blocks_mut()[BasicBlock::from_usize(i)] = block; + } + + Ok(()) + } + + fn parse_block_decls(&mut self, stmts: impl Iterator) -> PResult<()> { + for stmt in stmts { + let (var, _, _) = self.parse_let_statement(stmt)?; + let data = BasicBlockData::new(None); + let block = self.body.basic_blocks_mut().push(data); + self.block_map.insert(var, block); + } + + Ok(()) + } + + fn parse_local_decls(&mut self, mut stmts: impl Iterator) -> PResult<()> { + let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?; + self.local_map.insert(ret_var, Local::from_u32(0)); + + for stmt in stmts { + let (var, ty, span) = self.parse_let_statement(stmt)?; + let decl = LocalDecl::new(ty, span); + let local = self.body.local_decls.push(decl); + self.local_map.insert(var, local); + } + + Ok(()) + } + + fn parse_let_statement(&mut self, stmt_id: StmtId) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { + let pattern = match &self.thir[stmt_id].kind { + StmtKind::Let { pattern, .. } => pattern, + StmtKind::Expr { expr, .. } => { + return Err(self.expr_error(*expr, "let statement")); + } + }; + + self.parse_var(pattern) + } + + fn parse_var(&mut self, mut pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { + // Make sure we throw out any `AscribeUserType` we find + loop { + match &pat.kind { + PatKind::Binding { var, ty, .. } => break Ok((*var, *ty, pat.span)), + PatKind::AscribeUserType { subpattern, .. } => { + pat = subpattern; + } + _ => { + break Err(ParseError { + span: pat.span, + item_description: format!("{:?}", pat.kind), + expected: "local".to_string(), + }); + } + } + } + } + + fn parse_block_def(&self, expr_id: ExprId) -> PResult> { + let block = parse_by_kind!(self, expr_id, "basic block", + ExprKind::Block { block } => &self.thir[*block], + ); + + let mut data = BasicBlockData::new(None); + for stmt_id in &*block.stmts { + let stmt = self.statement_as_expr(*stmt_id)?; + let statement = self.parse_statement(stmt)?; + data.statements.push(Statement { source_info: self.source_info, kind: statement }); + } + + let Some(trailing) = block.expr else { + return Err(self.expr_error(expr_id, "terminator")) + }; + let terminator = self.parse_terminator(trailing)?; + data.terminator = Some(Terminator { source_info: self.source_info, kind: terminator }); + + Ok(data) + } +} diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs new file mode 100644 index 0000000000000..6d6176584f5f4 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -0,0 +1,72 @@ +use rustc_middle::{mir::*, thir::*, ty}; + +use super::{parse_by_kind, PResult, ParseCtxt}; + +impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { + pub fn parse_statement(&self, expr_id: ExprId) -> PResult> { + parse_by_kind!(self, expr_id, "statement", + @call("mir_retag", args) => { + Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?))) + }, + @call("mir_retag_raw", args) => { + Ok(StatementKind::Retag(RetagKind::Raw, Box::new(self.parse_place(args[0])?))) + }, + ExprKind::Assign { lhs, rhs } => { + let lhs = self.parse_place(*lhs)?; + let rhs = self.parse_rvalue(*rhs)?; + Ok(StatementKind::Assign(Box::new((lhs, rhs)))) + }, + ) + } + + pub fn parse_terminator(&self, expr_id: ExprId) -> PResult> { + parse_by_kind!(self, expr_id, "terminator", + @call("mir_return", _args) => { + Ok(TerminatorKind::Return) + }, + @call("mir_goto", args) => { + Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } ) + }, + ) + } + + fn parse_rvalue(&self, expr_id: ExprId) -> PResult> { + parse_by_kind!(self, expr_id, "rvalue", + ExprKind::Borrow { borrow_kind, arg } => Ok( + Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) + ), + ExprKind::AddressOf { mutability, arg } => Ok( + Rvalue::AddressOf(*mutability, self.parse_place(*arg)?) + ), + _ => self.parse_operand(expr_id).map(Rvalue::Use), + ) + } + + fn parse_operand(&self, expr_id: ExprId) -> PResult> { + parse_by_kind!(self, expr_id, "operand", + @call("mir_move", args) => self.parse_place(args[0]).map(Operand::Move), + _ => self.parse_place(expr_id).map(Operand::Copy), + ) + } + + fn parse_place(&self, expr_id: ExprId) -> PResult> { + parse_by_kind!(self, expr_id, "place", + ExprKind::Deref { arg } => Ok( + self.parse_place(*arg)?.project_deeper(&[PlaceElem::Deref], self.tcx) + ), + _ => self.parse_local(expr_id).map(Place::from), + ) + } + + fn parse_local(&self, expr_id: ExprId) -> PResult { + parse_by_kind!(self, expr_id, "local", + ExprKind::VarRef { id } => Ok(self.local_map[id]), + ) + } + + fn parse_block(&self, expr_id: ExprId) -> PResult { + parse_by_kind!(self, expr_id, "basic block", + ExprKind::VarRef { id } => Ok(self.block_map[id]), + ) + } +} diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index cbcf9cd129f3f..437ac8d82a1a3 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -481,6 +481,22 @@ fn construct_fn<'tcx>( (None, fn_sig.output()) }; + if let Some(custom_mir_attr) = + tcx.hir().attrs(fn_id).iter().find(|attr| attr.name_or_empty() == sym::custom_mir) + { + return custom::build_custom_mir( + tcx, + fn_def.did.to_def_id(), + thir, + expr, + arguments, + return_ty, + return_ty_span, + span, + custom_mir_attr, + ); + } + let infcx = tcx.infer_ctxt().build(); let mut builder = Builder::new( thir, @@ -1033,6 +1049,7 @@ pub(crate) fn parse_float_into_scalar( mod block; mod cfg; +mod custom; mod expr; mod matches; mod misc; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c7a7c3e3fa8ee..c4639d3a513df 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -51,11 +51,17 @@ impl<'tcx> Cx<'tcx> { trace!(?expr.ty); // Now apply adjustments, if any. - for adjustment in self.typeck_results.expr_adjustments(hir_expr) { - trace!(?expr, ?adjustment); - let span = expr.span; - expr = - self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span)); + if self.apply_adjustments { + for adjustment in self.typeck_results.expr_adjustments(hir_expr) { + trace!(?expr, ?adjustment); + let span = expr.span; + expr = self.apply_adjustment( + hir_expr, + expr, + adjustment, + adjustment_span.unwrap_or(span), + ); + } } trace!(?expr.ty, "after adjustments"); diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 1d95d6b53fbec..b5c4b7b137d4c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -80,6 +80,9 @@ struct Cx<'tcx> { /// for the receiver. adjustment_span: Option<(HirId, Span)>, + /// False to indicate that adjustments should not be applied. Only used for `custom_mir` + apply_adjustments: bool, + /// The `DefId` of the owner of this body. body_owner: DefId, } @@ -87,6 +90,8 @@ struct Cx<'tcx> { impl<'tcx> Cx<'tcx> { fn new(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam) -> Cx<'tcx> { let typeck_results = tcx.typeck_opt_const_arg(def); + let did = def.did; + let hir = tcx.hir(); Cx { tcx, thir: Thir::new(), @@ -94,8 +99,12 @@ impl<'tcx> Cx<'tcx> { region_scope_tree: tcx.region_scope_tree(def.did), typeck_results, rvalue_scopes: &typeck_results.rvalue_scopes, - body_owner: def.did.to_def_id(), + body_owner: did.to_def_id(), adjustment_span: None, + apply_adjustments: hir + .attrs(hir.local_def_id_to_hir_id(did)) + .iter() + .all(|attr| attr.name_or_empty() != rustc_span::sym::custom_mir), } } diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 4730be1244bda..bfc2642fcc837 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -472,6 +472,14 @@ fn unsafety_check_result<'tcx>( // `mir_built` force this. let body = &tcx.mir_built(def).borrow(); + if body.should_skip() { + return tcx.arena.alloc(UnsafetyCheckResult { + violations: Vec::new(), + used_unsafe_blocks: FxHashSet::default(), + unused_unsafes: Some(Vec::new()), + }); + } + let param_env = tcx.param_env(def.did); let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env); diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 230c6a7cb4b00..27dbc3e22c97a 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -96,45 +96,48 @@ fn run_passes_inner<'tcx>( phase_change: Option, validate_each: bool, ) { - let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir; + let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir & !body.should_skip(); let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); - for pass in passes { - let name = pass.name(); - - let overridden = - overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| { - trace!( - pass = %name, - "{} as requested by flag", - if *polarity { "Running" } else { "Not running" }, - ); - *polarity - }); - if !overridden.unwrap_or_else(|| pass.is_enabled(&tcx.sess)) { - continue; + if !body.should_skip() { + for pass in passes { + let name = pass.name(); + + let overridden = overridden_passes.iter().rev().find(|(s, _)| s == &*name).map( + |(_name, polarity)| { + trace!( + pass = %name, + "{} as requested by flag", + if *polarity { "Running" } else { "Not running" }, + ); + *polarity + }, + ); + if !overridden.unwrap_or_else(|| pass.is_enabled(&tcx.sess)) { + continue; + } + + let dump_enabled = pass.is_mir_dump_enabled(); + + if dump_enabled { + dump_mir_for_pass(tcx, body, &name, false); + } + if validate { + validate_body(tcx, body, format!("before pass {}", name)); + } + + pass.run_pass(tcx, body); + + if dump_enabled { + dump_mir_for_pass(tcx, body, &name, true); + } + if validate { + validate_body(tcx, body, format!("after pass {}", name)); + } + + body.pass_count += 1; } - - let dump_enabled = pass.is_mir_dump_enabled(); - - if dump_enabled { - dump_mir_for_pass(tcx, body, &name, false); - } - if validate { - validate_body(tcx, body, format!("before pass {}", name)); - } - - pass.run_pass(tcx, body); - - if dump_enabled { - dump_mir_for_pass(tcx, body, &name, true); - } - if validate { - validate_body(tcx, body, format!("after pass {}", name)); - } - - body.pass_count += 1; } if let Some(new_phase) = phase_change { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3fe79370c374d..e7c0dbe67d77f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -584,6 +584,7 @@ symbols! { custom_attribute, custom_derive, custom_inner_attributes, + custom_mir, custom_test_frameworks, d, d32, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 29f796fad6d50..9f47a0a488668 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -57,6 +57,9 @@ use crate::marker::DiscriminantKind; use crate::mem; +#[cfg(not(bootstrap))] +pub mod mir; + // These imports are used for simplifying intra-doc links #[allow(unused_imports)] #[cfg(all(target_has_atomic = "8", target_has_atomic = "32", target_has_atomic = "ptr"))] diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs new file mode 100644 index 0000000000000..1bacdc39148a1 --- /dev/null +++ b/library/core/src/intrinsics/mir.rs @@ -0,0 +1,123 @@ +//! Rustc internal tooling for hand-writing MIR. +//! +//! If for some reasons you are not writing rustc tests and have found yourself considering using +//! this feature, turn back. This is *exceptionally* unstable. There is no attempt at all to make +//! anything work besides those things which the rustc test suite happened to need. If you make a +//! typo you'll probably ICE. Really, this is not the solution to your problems. Consider instead +//! supporting the [stable MIR project group](https://github.com/rust-lang/project-stable-mir). +//! +//! The documentation for this module describes how to use this feature. If you are interested in +//! hacking on the implementation, most of that documentation lives at +//! `rustc_mir_building/src/build/custom/mod.rs`. +//! +//! Typical usage will look like this: +//! +//! ```rust +//! #![feature(core_intrinsics, custom_mir)] +//! +//! extern crate core; +//! use core::intrinsics::mir::*; +//! +//! #[custom_mir(dialect = "built")] +//! pub fn simple(x: i32) -> i32 { +//! mir!( +//! let temp1: i32; +//! let temp2: _; +//! +//! { +//! temp1 = x; +//! Goto(exit) +//! } +//! +//! exit = { +//! temp2 = Move(temp1); +//! RET = temp2; +//! Return() +//! } +//! ) +//! } +//! ``` +//! +//! Hopefully most of this is fairly self-explanatory. Expanding on some notable details: +//! +//! - The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This +//! attribute only works on functions - there is no way to insert custom MIR into the middle of +//! another function. +//! - The `dialect` and `phase` parameters indicate which version of MIR you are inserting here. +//! This will normally be the phase that corresponds to the thing you are trying to test. The +//! phase can be omitted for dialects that have just one. +//! - You should define your function signature like you normally would. Externally, this function +//! can be called like any other function. +//! - Type inference works - you don't have to spell out the type of all of your locals. +//! +//! For now, all statements and terminators are parsed from nested invocations of the special +//! functions provided in this module. We additionally want to (but do not yet) support more +//! "normal" Rust syntax in places where it makes sense. Also, most kinds of instructions are not +//! supported yet. +//! + +#![unstable( + feature = "custom_mir", + reason = "MIR is an implementation detail and extremely unstable", + issue = "none" +)] +#![allow(unused_variables, non_snake_case, missing_debug_implementations)] + +/// Type representing basic blocks. +/// +/// All terminators will have this type as a return type. It helps achieve some type safety. +pub struct BasicBlock; + +macro_rules! define { + ($name:literal, $($sig:tt)*) => { + #[rustc_diagnostic_item = $name] + pub $($sig)* { panic!() } + } +} + +define!("mir_return", fn Return() -> BasicBlock); +define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock); +define!("mir_retag", fn Retag(place: T)); +define!("mir_retag_raw", fn RetagRaw(place: T)); +define!("mir_move", fn Move(place: T) -> T); + +/// Convenience macro for generating custom MIR. +/// +/// See the module documentation for syntax details. This macro is not magic - it only transforms +/// your MIR into something that is easier to parse in the compiler. +#[rustc_macro_transparency = "transparent"] +pub macro mir { + ( + $(let $local_decl:ident $(: $local_decl_ty:ty)? ;)* + + $entry_block:block + + $( + $block_name:ident = $block:block + )* + ) => {{ + // First, we declare all basic blocks. + $( + let $block_name: ::core::intrinsics::mir::BasicBlock; + )* + + { + // Now all locals + #[allow(non_snake_case)] + let RET; + $( + let $local_decl $(: $local_decl_ty)? ; + )* + + { + // Finally, the contents of the basic blocks + $entry_block; + $( + $block; + )* + + RET + } + } + }} +} diff --git a/src/test/mir-opt/building/custom/references.immut_ref.mir_map.0.mir b/src/test/mir-opt/building/custom/references.immut_ref.mir_map.0.mir new file mode 100644 index 0000000000000..71471f42fee61 --- /dev/null +++ b/src/test/mir-opt/building/custom/references.immut_ref.mir_map.0.mir @@ -0,0 +1,14 @@ +// MIR for `immut_ref` 0 mir_map + +fn immut_ref(_1: &i32) -> &i32 { + let mut _0: &i32; // return place in scope 0 at $DIR/references.rs:+0:30: +0:34 + let mut _2: *const i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL + + bb0: { + _2 = &raw const (*_1); // scope 0 at $DIR/references.rs:+0:1: +0:34 + Retag([raw] _2); // scope 0 at $DIR/references.rs:+0:1: +0:34 + _0 = &(*_2); // scope 0 at $DIR/references.rs:+0:1: +0:34 + Retag(_0); // scope 0 at $DIR/references.rs:+0:1: +0:34 + return; // scope 0 at $DIR/references.rs:+0:1: +0:34 + } +} diff --git a/src/test/mir-opt/building/custom/references.mut_ref.mir_map.0.mir b/src/test/mir-opt/building/custom/references.mut_ref.mir_map.0.mir new file mode 100644 index 0000000000000..19f191c29bd91 --- /dev/null +++ b/src/test/mir-opt/building/custom/references.mut_ref.mir_map.0.mir @@ -0,0 +1,14 @@ +// MIR for `mut_ref` 0 mir_map + +fn mut_ref(_1: &mut i32) -> &mut i32 { + let mut _0: &mut i32; // return place in scope 0 at $DIR/references.rs:+0:32: +0:40 + let mut _2: *mut i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL + + bb0: { + _2 = &raw mut (*_1); // scope 0 at $DIR/references.rs:+0:1: +0:40 + Retag([raw] _2); // scope 0 at $DIR/references.rs:+0:1: +0:40 + _0 = &mut (*_2); // scope 0 at $DIR/references.rs:+0:1: +0:40 + Retag(_0); // scope 0 at $DIR/references.rs:+0:1: +0:40 + return; // scope 0 at $DIR/references.rs:+0:1: +0:40 + } +} diff --git a/src/test/mir-opt/building/custom/references.rs b/src/test/mir-opt/building/custom/references.rs new file mode 100644 index 0000000000000..f08ecff0be8c7 --- /dev/null +++ b/src/test/mir-opt/building/custom/references.rs @@ -0,0 +1,43 @@ +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; +use core::ptr::{addr_of, addr_of_mut}; + +// EMIT_MIR references.mut_ref.mir_map.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn mut_ref(x: &mut i32) -> &mut i32 { + mir!( + let t: *mut i32; + + { + t = addr_of_mut!(*x); + RetagRaw(t); + RET = &mut *t; + Retag(RET); + Return() + } + ) +} + +// EMIT_MIR references.immut_ref.mir_map.0.mir +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn immut_ref(x: &i32) -> &i32 { + mir!( + let t: *const i32; + + { + t = addr_of!(*x); + RetagRaw(t); + RET = & *t; + Retag(RET); + Return() + } + ) +} + +fn main() { + let mut x = 5; + assert_eq!(*mut_ref(&mut x), 5); + assert_eq!(*immut_ref(&x), 5); +} diff --git a/src/test/mir-opt/building/custom/simple_assign.rs b/src/test/mir-opt/building/custom/simple_assign.rs new file mode 100644 index 0000000000000..a7cdfa65f40a2 --- /dev/null +++ b/src/test/mir-opt/building/custom/simple_assign.rs @@ -0,0 +1,37 @@ +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; + +// EMIT_MIR simple_assign.simple.mir_map.0.mir +#[custom_mir(dialect = "built")] +pub fn simple(x: i32) -> i32 { + mir!( + let temp1: i32; + let temp2: _; + + { + temp1 = x; + Goto(exit) + } + + exit = { + temp2 = Move(temp1); + RET = temp2; + Return() + } + ) +} + +// EMIT_MIR simple_assign.simple_ref.mir_map.0.mir +#[custom_mir(dialect = "built")] +pub fn simple_ref(x: &mut i32) -> &mut i32 { + mir!({ + RET = Move(x); + Return() + }) +} + +fn main() { + assert_eq!(5, simple(5)); +} diff --git a/src/test/mir-opt/building/custom/simple_assign.simple.mir_map.0.mir b/src/test/mir-opt/building/custom/simple_assign.simple.mir_map.0.mir new file mode 100644 index 0000000000000..4fe0b2d95764e --- /dev/null +++ b/src/test/mir-opt/building/custom/simple_assign.simple.mir_map.0.mir @@ -0,0 +1,18 @@ +// MIR for `simple` 0 mir_map + +fn simple(_1: i32) -> i32 { + let mut _0: i32; // return place in scope 0 at $DIR/simple_assign.rs:+0:26: +0:29 + let mut _2: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL + let mut _3: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL + + bb0: { + _2 = _1; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29 + goto -> bb1; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29 + } + + bb1: { + _3 = move _2; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29 + _0 = _3; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29 + return; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:29 + } +} diff --git a/src/test/mir-opt/building/custom/simple_assign.simple_ref.mir_map.0.mir b/src/test/mir-opt/building/custom/simple_assign.simple_ref.mir_map.0.mir new file mode 100644 index 0000000000000..0ae6b7e4dbfc9 --- /dev/null +++ b/src/test/mir-opt/building/custom/simple_assign.simple_ref.mir_map.0.mir @@ -0,0 +1,10 @@ +// MIR for `simple_ref` 0 mir_map + +fn simple_ref(_1: &mut i32) -> &mut i32 { + let mut _0: &mut i32; // return place in scope 0 at $DIR/simple_assign.rs:+0:35: +0:43 + + bb0: { + _0 = move _1; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:43 + return; // scope 0 at $DIR/simple_assign.rs:+0:1: +0:43 + } +} diff --git a/src/test/ui/feature-gates/feature-gate-custom_mir.rs b/src/test/ui/feature-gates/feature-gate-custom_mir.rs new file mode 100644 index 0000000000000..0126dde2f7d4b --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-custom_mir.rs @@ -0,0 +1,12 @@ +#![feature(core_intrinsics)] + +extern crate core; + +#[custom_mir(dialect = "built")] //~ ERROR the `#[custom_mir]` attribute is just used for the Rust test suite +pub fn foo(_x: i32) -> i32 { + 0 +} + +fn main() { + assert_eq!(2, foo(2)); +} diff --git a/src/test/ui/feature-gates/feature-gate-custom_mir.stderr b/src/test/ui/feature-gates/feature-gate-custom_mir.stderr new file mode 100644 index 0000000000000..3c149d30d82bd --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-custom_mir.stderr @@ -0,0 +1,11 @@ +error[E0658]: the `#[custom_mir]` attribute is just used for the Rust test suite + --> $DIR/feature-gate-custom_mir.rs:5:1 + | +LL | #[custom_mir(dialect = "built")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(custom_mir)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. From a2037e3012ed6c59e91fadb693b35fd503cba117 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 1 Nov 2022 01:34:59 +0000 Subject: [PATCH 03/22] Check for substs compatibility for RPITITs --- .../src/traits/project.rs | 18 +++++++++++++++--- .../impl-trait/in-trait/generics-mismatch.rs | 17 +++++++++++++++++ .../in-trait/generics-mismatch.stderr | 12 ++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/impl-trait/in-trait/generics-mismatch.rs create mode 100644 src/test/ui/impl-trait/in-trait/generics-mismatch.stderr diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index c8276854016f0..ad216e5502d82 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2192,7 +2192,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( // Verify that the trait item and its implementation have compatible substs lists fn check_substs_compatible<'tcx>( tcx: TyCtxt<'tcx>, - assoc_ty: &ty::AssocItem, + assoc_item: &ty::AssocItem, substs: ty::SubstsRef<'tcx>, ) -> bool { fn check_substs_compatible_inner<'tcx>( @@ -2224,7 +2224,10 @@ fn check_substs_compatible<'tcx>( true } - check_substs_compatible_inner(tcx, tcx.generics_of(assoc_ty.def_id), substs.as_slice()) + let generics = tcx.generics_of(assoc_item.def_id); + // Chop off any additional substs (RPITIT) substs + let substs = &substs[0..generics.count().min(substs.len())]; + check_substs_compatible_inner(tcx, generics, substs) } fn confirm_impl_trait_in_trait_candidate<'tcx>( @@ -2253,12 +2256,21 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( }; } - let impl_fn_def_id = leaf_def.item.def_id; // Rebase from {trait}::{fn}::{opaque} to {impl}::{fn}::{opaque}, // since `data.substs` are the impl substs. let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs); + if !check_substs_compatible(tcx, &leaf_def.item, impl_fn_substs) { + let err = tcx.ty_error_with_message( + obligation.cause.span, + "impl method and trait method have different parameters", + ); + return Progress { term: err.into(), obligations }; + } + + let impl_fn_def_id = leaf_def.item.def_id; + let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, diff --git a/src/test/ui/impl-trait/in-trait/generics-mismatch.rs b/src/test/ui/impl-trait/in-trait/generics-mismatch.rs new file mode 100644 index 0000000000000..cc0fc720ebbfd --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/generics-mismatch.rs @@ -0,0 +1,17 @@ +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +struct U; + +trait Foo { + fn bar(&self) -> impl Sized; +} + +impl Foo for U { + fn bar(&self) {} + //~^ ERROR method `bar` has 1 type parameter but its trait declaration has 0 type parameters +} + +fn main() { + U.bar(); +} diff --git a/src/test/ui/impl-trait/in-trait/generics-mismatch.stderr b/src/test/ui/impl-trait/in-trait/generics-mismatch.stderr new file mode 100644 index 0000000000000..cd42683e0224d --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/generics-mismatch.stderr @@ -0,0 +1,12 @@ +error[E0049]: method `bar` has 1 type parameter but its trait declaration has 0 type parameters + --> $DIR/generics-mismatch.rs:11:12 + | +LL | fn bar(&self) -> impl Sized; + | - expected 0 type parameters +... +LL | fn bar(&self) {} + | ^ found 1 type parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0049`. From 32dae918a181e01b8ee01d256c1e54ddd6d0ea76 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 1 Nov 2022 01:36:50 +0000 Subject: [PATCH 04/22] Remap RPITIT substs properly --- .../src/traits/project.rs | 7 ++++++ .../in-trait/specialization-substs-remap.rs | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/test/ui/impl-trait/in-trait/specialization-substs-remap.rs diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index ad216e5502d82..474ae22e3d589 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2260,6 +2260,13 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( // since `data.substs` are the impl substs. let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs); + let impl_fn_substs = translate_substs( + selcx.infcx(), + obligation.param_env, + data.impl_def_id, + impl_fn_substs, + leaf_def.defining_node, + ); if !check_substs_compatible(tcx, &leaf_def.item, impl_fn_substs) { let err = tcx.ty_error_with_message( diff --git a/src/test/ui/impl-trait/in-trait/specialization-substs-remap.rs b/src/test/ui/impl-trait/in-trait/specialization-substs-remap.rs new file mode 100644 index 0000000000000..c9ee877db8ec5 --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/specialization-substs-remap.rs @@ -0,0 +1,24 @@ +// check-pass + +#![feature(specialization)] +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +trait Foo { + fn bar(&self) -> impl Sized; +} + +impl Foo for U +where + U: Copy, +{ + fn bar(&self) -> U { + *self + } +} + +impl Foo for i32 {} + +fn main() { + let _: i32 = 1i32.bar(); +} From 0f632c8f7882a4470bf1c7b6a4d6b14379b8e163 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 1 Nov 2022 01:42:47 +0000 Subject: [PATCH 05/22] Fix ICE in default impl error reporting --- compiler/rustc_middle/src/ty/error.rs | 4 ++- .../in-trait/specialization-broken.rs | 26 +++++++++++++++++++ .../in-trait/specialization-broken.stderr | 23 ++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/impl-trait/in-trait/specialization-broken.rs create mode 100644 src/test/ui/impl-trait/in-trait/specialization-broken.stderr diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 4e6cdb786025e..dc13374f992eb 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -430,7 +430,9 @@ impl<'tcx> TyCtxt<'tcx> { (ty::Projection(_), ty::Projection(_)) => { diag.note("an associated type was expected, but a different one was found"); } - (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { + (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) + if self.def_kind(proj.item_def_id) != DefKind::ImplTraitPlaceholder => + { let generics = self.generics_of(body_owner_def_id); let p_span = self.def_span(generics.type_param(p, self).def_id); if !sp.contains(p_span) { diff --git a/src/test/ui/impl-trait/in-trait/specialization-broken.rs b/src/test/ui/impl-trait/in-trait/specialization-broken.rs new file mode 100644 index 0000000000000..9d27d3710a601 --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/specialization-broken.rs @@ -0,0 +1,26 @@ +// FIXME(compiler-errors): I'm not exactly sure if this is expected to pass or not. +// But we fixed an ICE anyways. + +#![feature(specialization)] +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +trait Foo { + fn bar(&self) -> impl Sized; +} + +default impl Foo for U +where + U: Copy, +{ + fn bar(&self) -> U { + //~^ ERROR method `bar` has an incompatible type for trait + *self + } +} + +impl Foo for i32 {} + +fn main() { + 1i32.bar(); +} diff --git a/src/test/ui/impl-trait/in-trait/specialization-broken.stderr b/src/test/ui/impl-trait/in-trait/specialization-broken.stderr new file mode 100644 index 0000000000000..a30e6346b2927 --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/specialization-broken.stderr @@ -0,0 +1,23 @@ +error[E0053]: method `bar` has an incompatible type for trait + --> $DIR/specialization-broken.rs:16:22 + | +LL | default impl Foo for U + | - this type parameter +... +LL | fn bar(&self) -> U { + | ^ + | | + | expected associated type, found type parameter `U` + | help: change the output type to match the trait: `impl Sized` + | +note: type in trait + --> $DIR/specialization-broken.rs:9:22 + | +LL | fn bar(&self) -> impl Sized; + | ^^^^^^^^^^ + = note: expected fn pointer `fn(&U) -> impl Sized` + found fn pointer `fn(&U) -> U` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0053`. From cf6efe8137d88237b7e280a22eaea2f2193da47d Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 25 Oct 2022 16:57:21 -0400 Subject: [PATCH 06/22] Add test case --- .../codegen/auxiliary/static_dllimport_aux.rs | 13 +++++++++++++ .../issue-81408-dllimport-thinlto-windows.rs | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/test/codegen/auxiliary/static_dllimport_aux.rs create mode 100644 src/test/codegen/issue-81408-dllimport-thinlto-windows.rs diff --git a/src/test/codegen/auxiliary/static_dllimport_aux.rs b/src/test/codegen/auxiliary/static_dllimport_aux.rs new file mode 100644 index 0000000000000..afb0dc42f443a --- /dev/null +++ b/src/test/codegen/auxiliary/static_dllimport_aux.rs @@ -0,0 +1,13 @@ +use std::sync::atomic::{AtomicPtr, Ordering}; + +#[inline(always)] +pub fn memrchr() { + fn detect() {} + + static CROSS_CRATE_STATIC_ITEM: AtomicPtr<()> = AtomicPtr::new(detect as *mut ()); + + unsafe { + let fun = CROSS_CRATE_STATIC_ITEM.load(Ordering::SeqCst); + std::mem::transmute::<*mut (), fn()>(fun)() + } +} diff --git a/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs b/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs new file mode 100644 index 0000000000000..e674c0561d3b8 --- /dev/null +++ b/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs @@ -0,0 +1,15 @@ +// compile-flags: -O -C lto=thin -C prefer-dynamic=no +// only-windows +// aux-build:static_dllimport_aux.rs + +// Test that on Windows, when performing ThinLTO, we do not mark cross-crate static items with +// dllimport because lld does not fix the symbol names for us. + +extern crate static_dllimport_aux; + +// CHECK-LABEL: @{{.+}}CROSS_CRATE_STATIC_ITEM{{.+}} = +// CHECK-SAME: external dllimport local_unnamed_addr global %"{{.+}}::AtomicPtr + +pub fn main() { + static_dllimport_aux::memrchr(); +} From 296489c89268e56abb8f6050842d006b16ed4f09 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 20 Oct 2022 17:45:09 -0400 Subject: [PATCH 07/22] Fix Access Violation when using lld & ThinLTO on windows-msvc Users report an AV at runtime of the compiled binary when using lld and ThinLTO on windows-msvc. The AV occurs when accessing a static value which is defined in one crate but used in another. Based on the disassembly of the cross-crate use, it appears that the use is not correctly linked with the definition and is instead assigned a garbage pointer value. If we look at the symbol tables for each crates' obj file, we can see what is happening: *lib.obj*: ``` COFF SYMBOL TABLE ... 00E 00000000 SECT2 notype External | _ZN10reproducer7memrchr2FN17h612b61ca0e168901E ... ``` *bin.obj*: ``` COFF SYMBOL TABLE ... 010 00000000 UNDEF notype External | __imp__ZN10reproducer7memrchr2FN17h612b61ca0e168901E ... ``` The use of the symbol has the "import" style symbol name but the declaration doesn't generate any symbol with the same name. As a result, linking the files generates a warning from lld: > rust-lld: warning: bin.obj: locally defined symbol imported: reproducer::memrchr::FN::h612b61ca0e168901 (defined in lib.obj) [LNK4217] and the symbol reference remains undefined at runtime leading to the AV. To fix this, we just need to detect that we are performing ThinLTO (and thus, static linking) and omit the `dllimport` attribute on the extern item in LLVM IR. --- compiler/rustc_codegen_llvm/src/consts.rs | 4 +++- src/test/codegen/issue-81408-dllimport-thinlto-windows.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index dd3c43ba5ca7c..bf5ac4e503e3f 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -19,6 +19,7 @@ use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; +use rustc_session::config::Lto; use rustc_target::abi::{ AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange, }; @@ -303,7 +304,8 @@ impl<'ll> CodegenCx<'ll, '_> { // ThinLTO can't handle this workaround in all cases, so we don't // emit the attrs. Instead we make them unnecessary by disallowing // dynamic linking when linker plugin based LTO is enabled. - !self.tcx.sess.opts.cg.linker_plugin_lto.enabled(); + !self.tcx.sess.opts.cg.linker_plugin_lto.enabled() && + self.tcx.sess.lto() != Lto::Thin; // If this assertion triggers, there's something wrong with commandline // argument validation. diff --git a/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs b/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs index e674c0561d3b8..0b6ab4f7ecb3b 100644 --- a/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs +++ b/src/test/codegen/issue-81408-dllimport-thinlto-windows.rs @@ -8,7 +8,7 @@ extern crate static_dllimport_aux; // CHECK-LABEL: @{{.+}}CROSS_CRATE_STATIC_ITEM{{.+}} = -// CHECK-SAME: external dllimport local_unnamed_addr global %"{{.+}}::AtomicPtr +// CHECK-SAME: external local_unnamed_addr global %"{{.+}}AtomicPtr pub fn main() { static_dllimport_aux::memrchr(); From f8e2cef5faa7ff77e91ea2d0416006f0b7aa52bf Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 3 Nov 2022 22:01:58 -0700 Subject: [PATCH 08/22] Move intra-doc link checks to a separate function. --- src/tools/linkchecker/main.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 7842611bd4ffa..4092722501d0e 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -365,6 +365,23 @@ impl Checker { } }); + self.check_intra_doc_links(file, &pretty_path, &source, report); + + // we don't need the source anymore, + // so drop to reduce memory-usage + match self.cache.get_mut(&pretty_path).unwrap() { + FileEntry::HtmlFile { source, .. } => *source = Rc::new(String::new()), + _ => unreachable!("must be html file"), + } + } + + fn check_intra_doc_links( + &mut self, + file: &Path, + pretty_path: &str, + source: &str, + report: &mut Report, + ) { // Search for intra-doc links that rustdoc didn't warn about // FIXME(#77199, 77200) Rustdoc should just warn about these directly. // NOTE: only looks at one line at a time; in practice this should find most links @@ -379,12 +396,6 @@ impl Checker { } } } - // we don't need the source anymore, - // so drop to reduce memory-usage - match self.cache.get_mut(&pretty_path).unwrap() { - FileEntry::HtmlFile { source, .. } => *source = Rc::new(String::new()), - _ => unreachable!("must be html file"), - } } /// Load a file from disk, or from the cache if available. From 57b229086e274c21e5544e6719271ae79a952194 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 3 Nov 2022 22:02:39 -0700 Subject: [PATCH 09/22] Remove reference from the intra-doc link checker. --- src/tools/linkchecker/main.rs | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 4092722501d0e..4170c32f1fe25 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -55,30 +55,6 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ #[rustfmt::skip] const INTRA_DOC_LINK_EXCEPTIONS: &[(&str, &[&str])] = &[ - // This will never have links that are not in other pages. - // To avoid repeating the exceptions twice, an empty list means all broken links are allowed. - ("reference/print.html", &[]), - // All the reference 'links' are actually ENBF highlighted as code - ("reference/comments.html", &[ - "/ !", - "* !", - ]), - ("reference/identifiers.html", &[ - "a-z A-Z", - "a-z A-Z 0-9 _", - "a-z A-Z] [a-z A-Z 0-9 _", - ]), - ("reference/tokens.html", &[ - "0-1", - "0-7", - "0-9", - "0-9", - "0-9 a-f A-F", - ]), - ("reference/notation.html", &[ - "b B", - "a-z", - ]), // This is being used in the sense of 'inclusive range', not a markdown link ("core/ops/struct.RangeInclusive.html", &["begin, end"]), ("std/ops/struct.RangeInclusive.html", &["begin, end"]), @@ -382,6 +358,16 @@ impl Checker { source: &str, report: &mut Report, ) { + let relative = file.strip_prefix(&self.root).expect("should always be relative to root"); + // Don't check the reference. It has several legitimate things that + // look like []. The reference has its own broken link + // checker in its CI which handles this using pulldown_cmark. + // + // This checks both the end of the root (when checking just the + // reference directory) or the beginning (when checking all docs). + if self.root.ends_with("reference") || relative.starts_with("reference") { + return; + } // Search for intra-doc links that rustdoc didn't warn about // FIXME(#77199, 77200) Rustdoc should just warn about these directly. // NOTE: only looks at one line at a time; in practice this should find most links From e49d10db14c618e2635d0e21b6e32cb2e1df78cf Mon Sep 17 00:00:00 2001 From: AndyJado <101876416+AndyJado@users.noreply.github.com> Date: Wed, 26 Oct 2022 10:42:05 +0800 Subject: [PATCH 10/22] first move on a nested span_label Apply suggestions from code review Co-authored-by: David Wood --- .../src/diagnostics/conflict_errors.rs | 14 ++++++--- .../rustc_borrowck/src/diagnostics/mod.rs | 20 +++++++++++++ .../rustc_borrowck/src/session_diagnostics.rs | 30 +++++++++++++++++++ .../locales/en-US/borrowck.ftl | 12 ++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8987a51757cd5..7f26e970e3094 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -724,13 +724,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_span, &self.describe_any_place(borrow.borrowed_place.as_ref()), ); - - borrow_spans.var_span_label( + borrow_spans.var_subdiag( &mut err, - { + |var_span| { + use crate::session_diagnostics::CaptureVarCause::*; let place = &borrow.borrowed_place; let desc_place = self.describe_any_place(place.as_ref()); - format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe()) + match borrow_spans { + UseSpans::ClosureUse { generator_kind, .. } => match generator_kind { + Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span }, + None => BorrowUsePlaceClosure { place: desc_place, var_span }, + }, + _ => BorrowUsePlace { place: desc_place, var_span }, + } }, "mutable", ); diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 534d9ecae6e6f..61518378e3d0c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -623,6 +623,26 @@ impl UseSpans<'_> { } } + /// Add a subdiagnostic to the use of the captured variable, if it exists. + pub(super) fn var_subdiag( + self, + err: &mut Diagnostic, + f: impl Fn(Span) -> crate::session_diagnostics::CaptureVarCause, + kind_desc: impl Into, + ) { + if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self { + if capture_kind_span == path_span { + err.subdiagnostic(f(capture_kind_span)); + } else { + err.subdiagnostic(crate::session_diagnostics::CaptureVarKind { + kind_desc: kind_desc.into(), + kind_span: capture_kind_span, + }); + err.subdiagnostic(f(path_span)); + } + } + } + /// Returns `false` if this place is not used in a closure. pub(super) fn for_closure(&self) -> bool { match *self { diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index fe24f85fae10a..824f20a31bb09 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -148,3 +148,33 @@ pub(crate) enum RequireStaticErr { multi_span: MultiSpan, }, } + +#[derive(Subdiagnostic)] +#[label(borrowck_capture_kind_label)] +pub(crate) struct CaptureVarKind { + pub kind_desc: String, + #[primary_span] + pub kind_span: Span, +} + +#[derive(Subdiagnostic)] +pub(crate) enum CaptureVarCause { + #[label(borrowck_var_borrow_by_use_place)] + BorrowUsePlace { + place: String, + #[primary_span] + var_span: Span, + }, + #[label(borrowck_var_borrow_by_use_place_in_generator)] + BorrowUsePlaceGenerator { + place: String, + #[primary_span] + var_span: Span, + }, + #[label(borrowck_var_borrow_by_use_place_in_closure)] + BorrowUsePlaceClosure { + place: String, + #[primary_span] + var_span: Span, + }, +} diff --git a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl index 67f2156f32e50..80fc4c6e4f5d3 100644 --- a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl @@ -58,3 +58,15 @@ borrowck_returned_lifetime_short = borrowck_used_impl_require_static = the used `impl` has a `'static` requirement + +borrowck_capture_kind_label = + capture is {$kind_desc} because of use here + +borrowck_var_borrow_by_use_place_in_generator = + borrow occurs due to use of {$place} in closure in generator + +borrowck_var_borrow_by_use_place_in_closure = + borrow occurs due to use of {$place} in closure + +borrowck_var_borrow_by_use_place = + borrow occurs due to use of {$place} From 13d4c61b5f732f17b66c7610588e113844fc5e08 Mon Sep 17 00:00:00 2001 From: Tanner Davies Date: Sat, 5 Nov 2022 15:07:10 -0600 Subject: [PATCH 11/22] Place config.toml in current working directory if config not found --- src/bootstrap/bin/main.rs | 2 +- src/bootstrap/config.rs | 4 ++-- src/bootstrap/setup.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 9b4861ccd95f4..be69f819c6428 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -35,7 +35,7 @@ fn main() { // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the // changelog warning, not the `x.py setup` message. - let suggest_setup = !config.config.exists() && !matches!(config.cmd, Subcommand::Setup { .. }); + let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. }); if suggest_setup { println!("warning: you have not made a `config.toml`"); println!( diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 21dc11c48081e..c086d248e5ce1 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -80,7 +80,7 @@ pub struct Config { pub keep_stage_std: Vec, pub src: PathBuf, /// defaults to `config.toml` - pub config: PathBuf, + pub config: Option, pub jobs: Option, pub cmd: Subcommand, pub incremental: bool, @@ -942,7 +942,7 @@ impl Config { } config.changelog_seen = toml.changelog_seen; - config.config = toml_path; + config.config = if toml_path.exists() { Some(toml_path) } else { None }; let build = toml.build.unwrap_or_default(); diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index eb7da1bda73cb..04480277fe047 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -82,7 +82,7 @@ impl fmt::Display for Profile { } pub fn setup(config: &Config, profile: Profile) { - let path = &config.config; + let path = &config.config.clone().unwrap_or(PathBuf::from("config.toml")); if path.exists() { eprintln!( From 17044c1d008e2d7c776e33a1e16d291dabd6520e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 7 Nov 2022 09:29:14 +0100 Subject: [PATCH 12/22] disable btree size tests on Miri --- library/alloc/src/collections/btree/node/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index aadb0dc9c40d9..64bce0ff8c048 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -94,6 +94,7 @@ fn test_partial_eq() { #[test] #[cfg(target_arch = "x86_64")] +#[cfg_attr(miri, ignore)] // We'd like to run Miri with layout randomization fn test_sizes() { assert_eq!(core::mem::size_of::>(), 16); assert_eq!(core::mem::size_of::>(), 16 + CAPACITY * 2 * 8); From 780952f922a52db435aff7b73cfdb6cb541083b8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 7 Nov 2022 10:33:54 +0100 Subject: [PATCH 13/22] run alloc benchmarks in Miri and fix UB --- library/alloc/src/alloc/tests.rs | 1 - .../alloc/src/collections/vec_deque/tests.rs | 34 +++++++++++-------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/alloc/tests.rs b/library/alloc/src/alloc/tests.rs index b2f0194599b28..1a5938fd34cf1 100644 --- a/library/alloc/src/alloc/tests.rs +++ b/library/alloc/src/alloc/tests.rs @@ -22,7 +22,6 @@ fn allocate_zeroed() { } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn alloc_owned_small(b: &mut Bencher) { b.iter(|| { let _: Box<_> = Box::new(10); diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 1f2daef213c3b..a103531a20f20 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -3,7 +3,6 @@ use core::iter::TrustedLen; use super::*; #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_push_back_100(b: &mut test::Bencher) { let mut deq = VecDeque::with_capacity(101); b.iter(|| { @@ -16,7 +15,6 @@ fn bench_push_back_100(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_push_front_100(b: &mut test::Bencher) { let mut deq = VecDeque::with_capacity(101); b.iter(|| { @@ -29,12 +27,15 @@ fn bench_push_front_100(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_pop_back_100(b: &mut test::Bencher) { - let mut deq = VecDeque::::with_capacity(101); + let size = 100; + let mut deq = VecDeque::::with_capacity(size+1); + // We'll mess with private state to pretend like `deq` is filled. + // Make sure the buffer is initialized so that we don't read uninit memory. + unsafe { deq.ptr().write_bytes(0u8, size+1) }; b.iter(|| { - deq.head = 100; + deq.head = size; deq.tail = 0; while !deq.is_empty() { test::black_box(deq.pop_back()); @@ -43,9 +44,9 @@ fn bench_pop_back_100(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_retain_whole_10000(b: &mut test::Bencher) { - let v = (1..100000).collect::>(); + let size = if cfg!(miri) { 1000 } else { 100000 }; + let v = (1..size).collect::>(); b.iter(|| { let mut v = v.clone(); @@ -54,9 +55,9 @@ fn bench_retain_whole_10000(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_retain_odd_10000(b: &mut test::Bencher) { - let v = (1..100000).collect::>(); + let size = if cfg!(miri) { 1000 } else { 100000 }; + let v = (1..size).collect::>(); b.iter(|| { let mut v = v.clone(); @@ -65,23 +66,26 @@ fn bench_retain_odd_10000(b: &mut test::Bencher) { } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_retain_half_10000(b: &mut test::Bencher) { - let v = (1..100000).collect::>(); + let size = if cfg!(miri) { 1000 } else { 100000 }; + let v = (1..size).collect::>(); b.iter(|| { let mut v = v.clone(); - v.retain(|x| *x > 50000) + v.retain(|x| *x > size/2) }) } #[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_pop_front_100(b: &mut test::Bencher) { - let mut deq = VecDeque::::with_capacity(101); + let size = 100; + let mut deq = VecDeque::::with_capacity(size+1); + // We'll mess with private state to pretend like `deq` is filled. + // Make sure the buffer is initialized so that we don't read uninit memory. + unsafe { deq.ptr().write_bytes(0u8, size+1) }; b.iter(|| { - deq.head = 100; + deq.head = size; deq.tail = 0; while !deq.is_empty() { test::black_box(deq.pop_front()); From 29d451ccb3adf8eecb42c68d532459e630e2096f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 7 Nov 2022 15:24:49 +0100 Subject: [PATCH 14/22] fmt --- library/alloc/src/collections/vec_deque/tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index a103531a20f20..6e0f83020f957 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -29,10 +29,10 @@ fn bench_push_front_100(b: &mut test::Bencher) { #[bench] fn bench_pop_back_100(b: &mut test::Bencher) { let size = 100; - let mut deq = VecDeque::::with_capacity(size+1); + let mut deq = VecDeque::::with_capacity(size + 1); // We'll mess with private state to pretend like `deq` is filled. // Make sure the buffer is initialized so that we don't read uninit memory. - unsafe { deq.ptr().write_bytes(0u8, size+1) }; + unsafe { deq.ptr().write_bytes(0u8, size + 1) }; b.iter(|| { deq.head = size; @@ -72,17 +72,17 @@ fn bench_retain_half_10000(b: &mut test::Bencher) { b.iter(|| { let mut v = v.clone(); - v.retain(|x| *x > size/2) + v.retain(|x| *x > size / 2) }) } #[bench] fn bench_pop_front_100(b: &mut test::Bencher) { let size = 100; - let mut deq = VecDeque::::with_capacity(size+1); + let mut deq = VecDeque::::with_capacity(size + 1); // We'll mess with private state to pretend like `deq` is filled. // Make sure the buffer is initialized so that we don't read uninit memory. - unsafe { deq.ptr().write_bytes(0u8, size+1) }; + unsafe { deq.ptr().write_bytes(0u8, size + 1) }; b.iter(|| { deq.head = size; From 4c3cad062014253452a15574868e49328486ec02 Mon Sep 17 00:00:00 2001 From: Kamil Koczurek Date: Mon, 7 Nov 2022 15:21:35 +0100 Subject: [PATCH 15/22] Add --print=split-debuginfo This option prints all supported values for -Csplit-debuginfo=.., i.e. only stable ones on stable/beta and all of them on nightly/dev. --- compiler/rustc_driver/src/lib.rs | 11 +++++++++++ compiler/rustc_session/src/config.rs | 2 ++ .../valid-print-requests/valid-print-requests.stderr | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index cf4bcc7c158fc..423f0cdddfd5c 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -736,6 +736,17 @@ fn print_crate_info( // Any output here interferes with Cargo's parsing of other printed output NativeStaticLibs => {} LinkArgs => {} + SplitDebuginfo => { + use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked}; + + for split in &[Off, Packed, Unpacked] { + let stable = sess.target.options.supported_split_debuginfo.contains(split); + let unstable_ok = sess.unstable_options(); + if stable || unstable_ok { + println!("{}", split); + } + } + } } } Compilation::Stop diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index aece29ca0cbf6..be084adb7b724 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -548,6 +548,7 @@ pub enum PrintRequest { NativeStaticLibs, StackProtectorStrategies, LinkArgs, + SplitDebuginfo, } pub enum Input { @@ -1806,6 +1807,7 @@ fn collect_print_requests( ("stack-protector-strategies", PrintRequest::StackProtectorStrategies), ("target-spec-json", PrintRequest::TargetSpec), ("link-args", PrintRequest::LinkArgs), + ("split-debuginfo", PrintRequest::SplitDebuginfo), ]; prints.extend(matches.opt_strs("print").into_iter().map(|req| { diff --git a/src/test/run-make/valid-print-requests/valid-print-requests.stderr b/src/test/run-make/valid-print-requests/valid-print-requests.stderr index 85782866d125a..5191e4676486a 100644 --- a/src/test/run-make/valid-print-requests/valid-print-requests.stderr +++ b/src/test/run-make/valid-print-requests/valid-print-requests.stderr @@ -1,2 +1,2 @@ -error: unknown print request `uwu`. Valid print requests are: `crate-name`, `file-names`, `sysroot`, `target-libdir`, `cfg`, `calling-conventions`, `target-list`, `target-cpus`, `target-features`, `relocation-models`, `code-models`, `tls-models`, `native-static-libs`, `stack-protector-strategies`, `target-spec-json`, `link-args` +error: unknown print request `uwu`. Valid print requests are: `crate-name`, `file-names`, `sysroot`, `target-libdir`, `cfg`, `calling-conventions`, `target-list`, `target-cpus`, `target-features`, `relocation-models`, `code-models`, `tls-models`, `native-static-libs`, `stack-protector-strategies`, `target-spec-json`, `link-args`, `split-debuginfo` From 66e8a296403e80ea50b2217d40e7a31fbd2f2158 Mon Sep 17 00:00:00 2001 From: Tanner Davies Date: Mon, 7 Nov 2022 15:27:42 -0700 Subject: [PATCH 16/22] Only set config.config to None when using default path --- src/bootstrap/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index c086d248e5ce1..ba50ce9ec2426 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -926,8 +926,10 @@ impl Config { // Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path, // but not if `config.toml` hasn't been created. let mut toml = if !using_default_path || toml_path.exists() { + config.config = Some(toml_path.clone()); get_toml(&toml_path) } else { + config.config = None; TomlConfig::default() }; @@ -942,7 +944,6 @@ impl Config { } config.changelog_seen = toml.changelog_seen; - config.config = if toml_path.exists() { Some(toml_path) } else { None }; let build = toml.build.unwrap_or_default(); From 1f21b96dce9c4165bcfae364b4e100953dd8167b Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 3 Nov 2022 14:15:17 +0800 Subject: [PATCH 17/22] add 'ty_error_with_guaranteed' and 'const_error_with_guaranteed' --- .../src/region_infer/opaque_types.rs | 4 +-- .../src/type_check/free_region_relations.rs | 5 ++-- compiler/rustc_borrowck/src/type_check/mod.rs | 4 +-- .../rustc_errors/src/diagnostic_builder.rs | 4 +-- .../rustc_hir_analysis/src/astconv/mod.rs | 26 +++++++++++-------- .../src/check/compare_method.rs | 4 +-- compiler/rustc_hir_analysis/src/collect.rs | 3 +-- .../rustc_hir_analysis/src/collect/type_of.rs | 4 +-- compiler/rustc_hir_typeck/src/coercion.rs | 4 +-- compiler/rustc_hir_typeck/src/expr.rs | 19 +++++++------- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 5 ++-- compiler/rustc_hir_typeck/src/op.rs | 4 +-- compiler/rustc_hir_typeck/src/pat.rs | 6 ++--- compiler/rustc_hir_typeck/src/place_op.rs | 7 +++-- compiler/rustc_middle/src/ty/context.rs | 16 ++++++++++++ 15 files changed, 68 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 103c7ed8ef701..dd222485daf2c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -299,8 +299,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { if errors.is_empty() { definition_ty } else { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); - self.tcx.ty_error() + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + self.tcx.ty_error_with_guaranteed(reported) } } } diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 02909592637d8..14cfc3613bf0c 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -247,12 +247,13 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .and(type_op::normalize::Normalize::new(ty)) .fully_perform(self.infcx) .unwrap_or_else(|_| { - self.infcx + let reported = self + .infcx .tcx .sess .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); TypeOpOutput { - output: self.infcx.tcx.ty_error(), + output: self.infcx.tcx.ty_error_with_guaranteed(reported), constraints: None, error_info: None, } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 50af229baaaed..9c1d0bb8b2357 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -233,11 +233,11 @@ pub(crate) fn type_check<'mir, 'tcx>( let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); if hidden_type.has_non_region_infer() { - infcx.tcx.sess.delay_span_bug( + let reported = infcx.tcx.sess.delay_span_bug( decl.hidden_type.span, &format!("could not resolve {:#?}", hidden_type.ty.kind()), ); - hidden_type.ty = infcx.tcx.ty_error(); + hidden_type.ty = infcx.tcx.ty_error_with_guaranteed(reported); } (opaque_type_key, (hidden_type, decl.origin)) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index ecf8570e81f71..730061fca9936 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -482,9 +482,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// In the meantime, though, callsites are required to deal with the "bug" /// locally in whichever way makes the most sense. #[track_caller] - pub fn delay_as_bug(&mut self) { + pub fn delay_as_bug(&mut self) -> G { self.downgrade_to_delayed_bug(); - self.emit(); + self.emit() } forward!( diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 8b1cc50a3a1f9..2665813478c2c 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1201,7 +1201,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (_, _) => { let got = if let Some(_) = term.ty() { "type" } else { "constant" }; let expected = def_kind.descr(assoc_item_def_id); - tcx.sess + let reported = tcx + .sess .struct_span_err( binding.span, &format!("expected {expected} bound, found {got}"), @@ -1212,11 +1213,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) .emit(); term = match def_kind { - hir::def::DefKind::AssocTy => tcx.ty_error().into(), + hir::def::DefKind::AssocTy => { + tcx.ty_error_with_guaranteed(reported).into() + } hir::def::DefKind::AssocConst => tcx - .const_error( + .const_error_with_guaranteed( tcx.bound_type_of(assoc_item_def_id) .subst(tcx, projection_ty.skip_binder().substs), + reported, ) .into(), _ => unreachable!(), @@ -1334,8 +1338,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|&(trait_ref, _, _)| trait_ref.def_id()) .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) .map(|trait_ref| tcx.def_span(trait_ref)); - tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); - return tcx.ty_error(); + let reported = + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); + return tcx.ty_error_with_guaranteed(reported); } // Check that there are no gross object safety violations; @@ -1345,14 +1350,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let object_safety_violations = astconv_object_safety_violations(tcx, item.trait_ref().def_id()); if !object_safety_violations.is_empty() { - report_object_safety_error( + let reported = report_object_safety_error( tcx, span, item.trait_ref().def_id(), &object_safety_violations, ) .emit(); - return tcx.ty_error(); + return tcx.ty_error_with_guaranteed(reported); } } @@ -2112,13 +2117,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "Type" }; - self.report_ambiguous_associated_type( + let reported = self.report_ambiguous_associated_type( span, type_name, &path_str, item_segment.ident.name, ); - return tcx.ty_error(); + return tcx.ty_error_with_guaranteed(reported) }; debug!("qpath_to_ty: self_type={:?}", self_ty); @@ -2560,8 +2565,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { err.span_note(impl_.self_ty.span, "not a concrete type"); } - err.emit(); - tcx.ty_error() + tcx.ty_error_with_guaranteed(err.emit()) } else { self.normalize_ty(span, ty) } diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 0e8ac17fb71b1..660c56ee8b011 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -611,11 +611,11 @@ pub fn collect_trait_impl_trait_tys<'tcx>( collected_tys.insert(def_id, ty); } Err(err) => { - tcx.sess.delay_span_bug( + let reported = tcx.sess.delay_span_bug( return_span, format!("could not fully resolve: {ty} => {err:?}"), ); - collected_tys.insert(def_id, tcx.ty_error()); + collected_tys.insert(def_id, tcx.ty_error_with_guaranteed(reported)); } } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 25faacadf3d0c..4bca16c3a1cce 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -512,8 +512,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { } _ => {} } - err.emit(); - self.tcx().ty_error() + self.tcx().ty_error_with_guaranteed(err.emit()) } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c29a645eb4a88..2402495c2e4a6 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -698,7 +698,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T } let Some(hidden) = locator.found else { - tcx.sess.emit_err(UnconstrainedOpaqueType { + let reported = tcx.sess.emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), what: match tcx.hir().get(scope) { @@ -708,7 +708,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T _ => "item", }, }); - return tcx.ty_error(); + return tcx.ty_error_with_guaranteed(reported); }; // Only check against typeck if we didn't already error diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 25306ebf35679..4d8ab2c1c7ad9 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1639,9 +1639,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); } - err.emit_unless(unsized_return); + let reported = err.emit_unless(unsized_return); - self.final_ty = Some(fcx.tcx.ty_error()); + self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported)); } } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 89b5e5161a9c2..e84734f46cb46 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -80,14 +80,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // coercions from ! to `expected`. if ty.is_never() { if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { - self.tcx().sess.delay_span_bug( + let reported = self.tcx().sess.delay_span_bug( expr.span, "expression with never type wound up being adjusted", ); return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { target.to_owned() } else { - self.tcx().ty_error() + self.tcx().ty_error_with_guaranteed(reported) }; } @@ -396,8 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } - err.emit(); - oprnd_t = tcx.ty_error(); + oprnd_t = tcx.ty_error_with_guaranteed(err.emit()); } } hir::UnOp::Not => { @@ -1097,12 +1096,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the assignment expression itself is ill-formed, don't // bother emitting another error - if lhs_ty.references_error() || rhs_ty.references_error() { + let reported = if lhs_ty.references_error() || rhs_ty.references_error() { err.delay_as_bug() } else { - err.emit(); - } - return self.tcx.ty_error(); + err.emit() + }; + return self.tcx.ty_error_with_guaranteed(reported); } let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); @@ -2777,8 +2776,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - err.emit(); - self.tcx.ty_error() + let reported = err.emit(); + self.tcx.ty_error_with_guaranteed(reported) } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 35323137e2d5a..6ed7a93d46332 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1212,9 +1212,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - - return (tcx.ty_error(), res); + let reported = err.emit(); + return (tcx.ty_error_with_guaranteed(reported), res); } } } else { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index c3bcbcc993b7c..38b3dd218a971 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -529,8 +529,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - self.tcx.ty_error() + let reported = err.emit(); + self.tcx.ty_error_with_guaranteed(reported) } }; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ea90da4a6dc35..c248deb892b1d 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1278,12 +1278,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_tys = tcx.mk_type_list(element_tys_iter); let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { - err.emit(); + let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error_with_guaranteed(reported)); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, tcx.ty_error(), def_bm, ti); + self.check_pat(elem, tcx.ty_error_with_guaranteed(reported), def_bm, ti); } tcx.mk_tup(element_tys_iter) } else { diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba8cf6926f30b..952ea14887f7b 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -90,8 +90,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - err.emit(); - Some((self.tcx.ty_error(), self.tcx.ty_error())) + let reported = err.emit(); + Some(( + self.tcx.ty_error_with_guaranteed(reported), + self.tcx.ty_error_with_guaranteed(reported), + )) } /// To type-check `base_expr[index_expr]`, we progressively autoderef diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fc706b890d5fb..4016a93caa869 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1283,6 +1283,12 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed` + #[track_caller] + pub fn ty_error_with_guaranteed(self, reported: ErrorGuaranteed) -> Ty<'tcx> { + self.mk_ty(Error(reported)) + } + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. #[track_caller] pub fn ty_error(self) -> Ty<'tcx> { @@ -1297,6 +1303,16 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_ty(Error(reported)) } + /// Like [TyCtxt::ty_error] but for constants, with current `ErrorGuaranteed` + #[track_caller] + pub fn const_error_with_guaranteed( + self, + ty: Ty<'tcx>, + reported: ErrorGuaranteed, + ) -> Const<'tcx> { + self.mk_const(ty::ConstS { kind: ty::ConstKind::Error(reported), ty }) + } + /// Like [TyCtxt::ty_error] but for constants. #[track_caller] pub fn const_error(self, ty: Ty<'tcx>) -> Const<'tcx> { From 465ac26405d32cc1dd73460fa465c9303f40ea43 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 4 Nov 2022 10:02:54 +0800 Subject: [PATCH 18/22] deprecate unchecked_claim_error_was_emitted in error_reported --- compiler/rustc_middle/src/ty/visit.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index f0e9f990a8115..5e366ef703f25 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -97,7 +97,11 @@ pub trait TypeVisitable<'tcx>: fmt::Debug + Clone { } fn error_reported(&self) -> Result<(), ErrorGuaranteed> { if self.references_error() { - Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) + if let Some(reported) = ty::tls::with(|tcx| tcx.sess.has_errors()) { + Err(reported) + } else { + bug!("expect tcx.sess.has_errors return true"); + } } else { Ok(()) } From 3320879f40034b32ef41f1c515baec5a1262f60d Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 4 Nov 2022 16:10:32 +0800 Subject: [PATCH 19/22] code cleanup with err.emit_unless --- compiler/rustc_hir_typeck/src/expr.rs | 6 +----- compiler/rustc_middle/src/ty/context.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e84734f46cb46..43669489e69bb 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1096,11 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the assignment expression itself is ill-formed, don't // bother emitting another error - let reported = if lhs_ty.references_error() || rhs_ty.references_error() { - err.delay_as_bug() - } else { - err.emit() - }; + let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()); return self.tcx.ty_error_with_guaranteed(reported); } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 4016a93caa869..50eece3011280 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1310,7 +1310,7 @@ impl<'tcx> TyCtxt<'tcx> { ty: Ty<'tcx>, reported: ErrorGuaranteed, ) -> Const<'tcx> { - self.mk_const(ty::ConstS { kind: ty::ConstKind::Error(reported), ty }) + self.mk_const(ty::ConstKind::Error(reported), ty) } /// Like [TyCtxt::ty_error] but for constants. From 667b15bb0ebb36683a2de31cff68ad2b5b0d3f57 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 27 Oct 2022 14:43:15 +0800 Subject: [PATCH 20/22] fix #103587, Recover from common if let syntax mistakes/typos --- .../locales/en-US/parser.ftl | 3 + .../src/infer/error_reporting/mod.rs | 74 +++++++++++++++++++ compiler/rustc_parse/src/errors.rs | 9 +++ compiler/rustc_parse/src/parser/expr.rs | 16 +++- src/test/ui/inference/issue-103587.rs | 12 +++ src/test/ui/inference/issue-103587.stderr | 40 ++++++++++ src/test/ui/suggestions/if-let-typo.stderr | 15 ++++ 7 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/inference/issue-103587.rs create mode 100644 src/test/ui/inference/issue-103587.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 455ff34f7247f..6d42b23fb3a21 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -125,6 +125,9 @@ parser_if_expression_missing_condition = missing condition for `if` expression parser_expected_expression_found_let = expected expression, found `let` statement +parser_expect_eq_instead_of_eqeq = expected `=`, found `==` + .suggestion = consider using `=` here + parser_expected_else_block = expected `{"{"}`, found {$first_tok} .label = expected an `if` or a block after this `else` .suggestion = add an `if` if this is the condition of a chained `else if` statement diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 3dbc9b9f3f938..ee9e59968be8f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! Error Reporting Code for the inference engine //! //! Because of the way inference, and in particular region inference, @@ -58,12 +59,14 @@ use crate::traits::{ StatementAsExpression, }; +use hir::intravisit::{walk_expr, walk_stmt}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::Node; use rustc_middle::dep_graph::DepContext; @@ -2333,6 +2336,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } } + // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, + // we try to suggest to add the missing `let` for `if let Some(..) = expr` + (ty::Bool, ty::Tuple(list)) => if list.len() == 0 { + self.suggest_let_for_letchains(&mut err, &trace.cause, span); + } _ => {} } } @@ -2357,6 +2365,72 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { diag } + /// Try to find code with pattern `if Some(..) = expr` + /// use a `visitor` to mark the `if` which its span contains given error span, + /// and then try to find a assignment in the `cond` part, which span is equal with error span + fn suggest_let_for_letchains( + &self, + err: &mut Diagnostic, + cause: &ObligationCause<'_>, + span: Span, + ) { + let hir = self.tcx.hir(); + let fn_hir_id = hir.get_parent_node(cause.body_id); + if let Some(node) = self.tcx.hir().find(fn_hir_id) && + let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_sig, _, body_id), .. + }) = node { + let body = hir.body(*body_id); + + /// Find the if expression with given span + struct IfVisitor { + pub result: bool, + pub found_if: bool, + pub err_span: Span, + } + + impl<'v> Visitor<'v> for IfVisitor { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if self.result { return; } + match ex.kind { + hir::ExprKind::If(cond, _, _) => { + self.found_if = true; + walk_expr(self, cond); + self.found_if = false; + } + _ => walk_expr(self, ex), + } + } + + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if let hir::StmtKind::Local(hir::Local { + span, pat: hir::Pat{..}, ty: None, init: Some(_), .. + }) = &ex.kind + && self.found_if + && span.eq(&self.err_span) { + self.result = true; + } + walk_stmt(self, ex); + } + + fn visit_body(&mut self, body: &'v hir::Body<'v>) { + hir::intravisit::walk_body(self, body); + } + } + + let mut visitor = IfVisitor { err_span: span, found_if: false, result: false }; + visitor.visit_body(&body); + if visitor.result { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider adding `let`", + "let ".to_string(), + Applicability::MachineApplicable, + ); + } + } + } + fn emit_tuple_wrap_err( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0924c8537159c..89d5fe3d9da1e 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -420,6 +420,15 @@ pub(crate) struct ExpectedExpressionFoundLet { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parser_expect_eq_instead_of_eqeq)] +pub(crate) struct ExpectedEqForLetExpr { + #[primary_span] + pub span: Span, + #[suggestion_verbose(applicability = "maybe-incorrect", code = "=")] + pub sugg_span: Span, +} + #[derive(Diagnostic)] #[diag(parser_expected_else_block)] pub(crate) struct ExpectedElseBlock { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4a1162b959983..565371668a8cd 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -9,9 +9,9 @@ use crate::errors::{ ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg, - DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet, - FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, - HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition, + DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr, + ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, + FoundExprWouldBeStmt, HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge, InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth, @@ -2334,7 +2334,15 @@ impl<'a> Parser<'a> { RecoverColon::Yes, CommaRecoveryMode::LikelyTuple, )?; - self.expect(&token::Eq)?; + if self.token == token::EqEq { + self.sess.emit_err(ExpectedEqForLetExpr { + span: self.token.span, + sugg_span: self.token.span, + }); + self.bump(); + } else { + self.expect(&token::Eq)?; + } let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) })?; diff --git a/src/test/ui/inference/issue-103587.rs b/src/test/ui/inference/issue-103587.rs new file mode 100644 index 0000000000000..11536f9f4cc48 --- /dev/null +++ b/src/test/ui/inference/issue-103587.rs @@ -0,0 +1,12 @@ +fn main() { + let x = Some(123); + + if let Some(_) == x {} + //~^ ERROR expected `=`, found `==` + + if Some(_) = x {} + //~^ ERROR mismatched types + + if None = x { } + //~^ ERROR mismatched types +} diff --git a/src/test/ui/inference/issue-103587.stderr b/src/test/ui/inference/issue-103587.stderr new file mode 100644 index 0000000000000..b373fbfbb948c --- /dev/null +++ b/src/test/ui/inference/issue-103587.stderr @@ -0,0 +1,40 @@ +error: expected `=`, found `==` + --> $DIR/issue-103587.rs:4:20 + | +LL | if let Some(_) == x {} + | ^^ + | +help: consider using `=` here + | +LL | if let Some(_) = x {} + | ~ + +error[E0308]: mismatched types + --> $DIR/issue-103587.rs:7:8 + | +LL | if Some(_) = x {} + | ^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | if let Some(_) = x {} + | +++ + +error[E0308]: mismatched types + --> $DIR/issue-103587.rs:10:8 + | +LL | if None = x { } + | ^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | if let None = x { } + | +++ +help: you might have meant to compare for equality + | +LL | if None == x { } + | + + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr index 3d9ac40ec367b..02148b7f7adfd 100644 --- a/src/test/ui/suggestions/if-let-typo.stderr +++ b/src/test/ui/suggestions/if-let-typo.stderr @@ -25,12 +25,22 @@ error[E0308]: mismatched types | LL | if Some(x) = foo {} | ^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | if let Some(x) = foo {} + | +++ error[E0308]: mismatched types --> $DIR/if-let-typo.rs:6:8 | LL | if Some(foo) = bar {} | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | if let Some(foo) = bar {} + | +++ error[E0308]: mismatched types --> $DIR/if-let-typo.rs:7:8 @@ -51,6 +61,11 @@ error[E0308]: mismatched types | LL | if Some(3) = foo {} | ^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | if let Some(3) = foo {} + | +++ error: aborting due to 7 previous errors From 9e7d2287cd87ad54cc3ed125ee4202fe870b380c Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 8 Nov 2022 16:04:14 +0800 Subject: [PATCH 21/22] use subdiagnostic for sugesting add let --- .../rustc_error_messages/locales/en-US/infer.ftl | 1 + compiler/rustc_infer/src/errors/mod.rs | 12 ++++++++++++ .../rustc_infer/src/infer/error_reporting/mod.rs | 8 ++------ compiler/rustc_parse/src/errors.rs | 2 +- src/test/ui/did_you_mean/issue-103909.stderr | 5 +++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index 18b3408b06ab4..fa975ff2c20cf 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -171,3 +171,4 @@ infer_msl_introduces_static = introduces a `'static` lifetime requirement infer_msl_unmet_req = because this has an unmet lifetime requirement infer_msl_trait_note = this has an implicit `'static` lifetime requirement infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement +infer_suggest_add_let_for_letchains = consider adding `let` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index bb04e1c49baea..ec4eeb8caa27c 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -180,6 +180,18 @@ pub enum SourceKindMultiSuggestion<'a> { }, } +#[derive(Subdiagnostic)] +#[suggestion( + infer_suggest_add_let_for_letchains, + style = "verbose", + applicability = "machine-applicable", + code = "let " +)] +pub(crate) struct SuggAddLetForLetChains { + #[primary_span] + pub span: Span, +} + impl<'a> SourceKindMultiSuggestion<'a> { pub fn new_fully_qualified( span: Span, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ee9e59968be8f..4d81de8b0b64f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -59,6 +59,7 @@ use crate::traits::{ StatementAsExpression, }; +use crate::errors::SuggAddLetForLetChains; use hir::intravisit::{walk_expr, walk_stmt}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg}; @@ -2421,12 +2422,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut visitor = IfVisitor { err_span: span, found_if: false, result: false }; visitor.visit_body(&body); if visitor.result { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider adding `let`", - "let ".to_string(), - Applicability::MachineApplicable, - ); + err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()}); } } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 89d5fe3d9da1e..e3acc11811f42 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -425,7 +425,7 @@ pub(crate) struct ExpectedExpressionFoundLet { pub(crate) struct ExpectedEqForLetExpr { #[primary_span] pub span: Span, - #[suggestion_verbose(applicability = "maybe-incorrect", code = "=")] + #[suggestion(applicability = "maybe-incorrect", code = "=", style = "verbose")] pub sugg_span: Span, } diff --git a/src/test/ui/did_you_mean/issue-103909.stderr b/src/test/ui/did_you_mean/issue-103909.stderr index a28914051b980..8641017470ba0 100644 --- a/src/test/ui/did_you_mean/issue-103909.stderr +++ b/src/test/ui/did_you_mean/issue-103909.stderr @@ -14,6 +14,11 @@ error[E0308]: mismatched types | LL | if Err(err) = File::open("hello.txt") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | if let Err(err) = File::open("hello.txt") { + | +++ error: aborting due to 2 previous errors From 9eb977bec440cf8610b9e687146c510f8a2fbf7c Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 8 Nov 2022 11:29:58 -0800 Subject: [PATCH 22/22] Add llvm-main to triagebot.toml --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 202f9cad57a43..d3f9efcc41c3e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -12,6 +12,7 @@ allow-unauthenticated = [ "T-*", "WG-*", "const-hack", + "llvm-main", "needs-fcp", "relnotes", "requires-nightly",