From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_mir_build/Cargo.toml | 1 + compiler/rustc_mir_build/src/build/block.rs | 6 +- compiler/rustc_mir_build/src/build/custom/mod.rs | 17 +- .../src/build/custom/parse/instruction.rs | 170 +++- .../rustc_mir_build/src/build/expr/as_constant.rs | 8 +- .../rustc_mir_build/src/build/expr/as_operand.rs | 4 +- .../rustc_mir_build/src/build/expr/as_place.rs | 4 +- .../rustc_mir_build/src/build/expr/as_rvalue.rs | 2 +- compiler/rustc_mir_build/src/build/expr/into.rs | 2 +- compiler/rustc_mir_build/src/build/expr/stmt.rs | 18 +- compiler/rustc_mir_build/src/build/matches/mod.rs | 9 +- compiler/rustc_mir_build/src/build/matches/test.rs | 31 +- compiler/rustc_mir_build/src/build/matches/util.rs | 1 - compiler/rustc_mir_build/src/build/mod.rs | 15 +- compiler/rustc_mir_build/src/build/scope.rs | 6 +- compiler/rustc_mir_build/src/check_unsafety.rs | 325 +++++--- compiler/rustc_mir_build/src/errors.rs | 865 +++++++++++++++++++++ compiler/rustc_mir_build/src/lib.rs | 3 +- compiler/rustc_mir_build/src/lints.rs | 21 +- compiler/rustc_mir_build/src/thir/constant.rs | 4 +- compiler/rustc_mir_build/src/thir/cx/mod.rs | 11 +- .../src/thir/pattern/check_match.rs | 445 ++++------- .../src/thir/pattern/const_to_pat.rs | 190 ++--- .../src/thir/pattern/deconstruct_pat.rs | 67 +- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 99 ++- .../rustc_mir_build/src/thir/pattern/usefulness.rs | 44 +- 26 files changed, 1625 insertions(+), 743 deletions(-) create mode 100644 compiler/rustc_mir_build/src/errors.rs (limited to 'compiler/rustc_mir_build') diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 2baa3bfcb..4ad3343d3 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -17,6 +17,7 @@ rustc_index = { path = "../rustc_index" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } rustc_infer = { path = "../rustc_infer" } +rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 49d7136a2..2643d33ce 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -231,7 +231,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { remainder_span, pattern, None, - Some((None, initializer_span)), + Some((Some(&destination), initializer_span)), ); this.visit_primary_bindings( pattern, @@ -373,7 +373,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the case of `!`, no return value is required, as the block will never return. // Opaque types of empty bodies also need this unit assignment, in order to infer that their // type is actually unit. Otherwise there will be no defining use found in the MIR. - if destination_ty.is_unit() || matches!(destination_ty.kind(), ty::Opaque(..)) { + if destination_ty.is_unit() + || matches!(destination_ty.kind(), ty::Alias(ty::Opaque, ..)) + { // We only want to assign an implicit `()` as the return value of the block if the // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.) this.cfg.push_assign_unit(block, source_info, destination, this.tcx); diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index eb021f477..33fdc1901 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -20,11 +20,12 @@ use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; +use rustc_hir::HirId; use rustc_index::vec::IndexVec; use rustc_middle::{ mir::*, thir::*, - ty::{Ty, TyCtxt}, + ty::{ParamEnv, Ty, TyCtxt}, }; use rustc_span::Span; @@ -33,6 +34,7 @@ mod parse; pub(super) fn build_custom_mir<'tcx>( tcx: TyCtxt<'tcx>, did: DefId, + hir_id: HirId, thir: &Thir<'tcx>, expr: ExprId, params: &IndexVec>, @@ -67,12 +69,16 @@ pub(super) fn build_custom_mir<'tcx>( parent_scope: None, inlined: None, inlined_parent_scope: None, - local_data: ClearCrossCrate::Clear, + local_data: ClearCrossCrate::Set(SourceScopeLocalData { + lint_root: hir_id, + safety: Safety::Safe, + }), }); body.injection_phase = Some(parse_attribute(attr)); let mut pctxt = ParseCtxt { tcx, + param_env: tcx.param_env(did), thir, source_scope: OUTERMOST_SOURCE_SCOPE, body: &mut body, @@ -80,10 +86,10 @@ pub(super) fn build_custom_mir<'tcx>( block_map: FxHashMap::default(), }; - let res = (|| { + let res: PResult<_> = try { pctxt.parse_args(¶ms)?; - pctxt.parse_body(expr) - })(); + pctxt.parse_body(expr)?; + }; if let Err(err) = res { tcx.sess.diagnostic().span_fatal( err.span, @@ -127,6 +133,7 @@ fn parse_attribute(attr: &Attribute) -> MirPhase { struct ParseCtxt<'tcx, 'body> { tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, thir: &'body Thir<'tcx>, source_scope: SourceScope, diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index 03206af33..0bca02589 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -1,16 +1,33 @@ use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::{mir::*, thir::*, ty}; +use rustc_span::Span; +use rustc_target::abi::VariantIdx; + +use crate::build::custom::ParseError; +use crate::build::expr::as_constant::as_constant_inner; 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_storage_live", args) => { + Ok(StatementKind::StorageLive(self.parse_local(args[0])?)) + }, + @call("mir_storage_dead", args) => { + Ok(StatementKind::StorageDead(self.parse_local(args[0])?)) + }, @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])?))) + @call("mir_set_discriminant", args) => { + let place = self.parse_place(args[0])?; + let var = self.parse_integer_literal(args[1])? as u32; + Ok(StatementKind::SetDiscriminant { + place: Box::new(place), + variant_index: VariantIdx::from_u32(var), + }) }, ExprKind::Assign { lhs, rhs } => { let lhs = self.parse_place(*lhs)?; @@ -21,18 +38,109 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { } pub fn parse_terminator(&self, expr_id: ExprId) -> PResult> { - parse_by_kind!(self, expr_id, _, "terminator", + parse_by_kind!(self, expr_id, expr, "terminator", @call("mir_return", _args) => { Ok(TerminatorKind::Return) }, @call("mir_goto", args) => { Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } ) }, + @call("mir_unreachable", _args) => { + Ok(TerminatorKind::Unreachable) + }, + @call("mir_drop", args) => { + Ok(TerminatorKind::Drop { + place: self.parse_place(args[0])?, + target: self.parse_block(args[1])?, + unwind: None, + }) + }, + @call("mir_drop_and_replace", args) => { + Ok(TerminatorKind::DropAndReplace { + place: self.parse_place(args[0])?, + value: self.parse_operand(args[1])?, + target: self.parse_block(args[2])?, + unwind: None, + }) + }, + @call("mir_call", args) => { + let destination = self.parse_place(args[0])?; + let target = self.parse_block(args[1])?; + self.parse_call(args[2], destination, target) + }, + ExprKind::Match { scrutinee, arms } => { + let discr = self.parse_operand(*scrutinee)?; + self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t }) + }, + ) + } + + fn parse_match(&self, arms: &[ArmId], span: Span) -> PResult { + let Some((otherwise, rest)) = arms.split_last() else { + return Err(ParseError { + span, + item_description: "no arms".to_string(), + expected: "at least one arm".to_string(), + }) + }; + + let otherwise = &self.thir[*otherwise]; + let PatKind::Wild = otherwise.pattern.kind else { + return Err(ParseError { + span: otherwise.span, + item_description: format!("{:?}", otherwise.pattern.kind), + expected: "wildcard pattern".to_string(), + }) + }; + let otherwise = self.parse_block(otherwise.body)?; + + let mut values = Vec::new(); + let mut targets = Vec::new(); + for arm in rest { + let arm = &self.thir[*arm]; + let PatKind::Constant { value } = arm.pattern.kind else { + return Err(ParseError { + span: arm.pattern.span, + item_description: format!("{:?}", arm.pattern.kind), + expected: "constant pattern".to_string(), + }) + }; + values.push(value.eval_bits(self.tcx, self.param_env, arm.pattern.ty)); + targets.push(self.parse_block(arm.body)?); + } + + Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise)) + } + + fn parse_call( + &self, + expr_id: ExprId, + destination: Place<'tcx>, + target: BasicBlock, + ) -> PResult> { + parse_by_kind!(self, expr_id, _, "function call", + ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => { + let fun = self.parse_operand(*fun)?; + let args = args + .iter() + .map(|arg| self.parse_operand(*arg)) + .collect::>>()?; + Ok(TerminatorKind::Call { + func: fun, + args, + destination, + target: Some(target), + cleanup: None, + from_hir_call: *from_hir_call, + fn_span: *fn_span, + }) + }, ) } fn parse_rvalue(&self, expr_id: ExprId) -> PResult> { parse_by_kind!(self, expr_id, _, "rvalue", + @call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant), ExprKind::Borrow { borrow_kind, arg } => Ok( Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) ), @@ -55,7 +163,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { | ExprKind::ConstParam { .. } | ExprKind::ConstBlock { .. } => { Ok(Operand::Constant(Box::new( - crate::build::expr::as_constant::as_constant_inner(expr, |_| None, self.tcx) + as_constant_inner(expr, |_| None, self.tcx) ))) }, _ => self.parse_place(expr_id).map(Operand::Copy), @@ -63,12 +171,42 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { } 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), - ) + self.parse_place_inner(expr_id).map(|(x, _)| x) + } + + fn parse_place_inner(&self, expr_id: ExprId) -> PResult<(Place<'tcx>, PlaceTy<'tcx>)> { + let (parent, proj) = parse_by_kind!(self, expr_id, expr, "place", + @call("mir_field", args) => { + let (parent, ty) = self.parse_place_inner(args[0])?; + let field = Field::from_u32(self.parse_integer_literal(args[1])? as u32); + let field_ty = ty.field_ty(self.tcx, field); + let proj = PlaceElem::Field(field, field_ty); + let place = parent.project_deeper(&[proj], self.tcx); + return Ok((place, PlaceTy::from_ty(field_ty))); + }, + @call("mir_variant", args) => { + (args[0], PlaceElem::Downcast( + None, + VariantIdx::from_u32(self.parse_integer_literal(args[1])? as u32) + )) + }, + ExprKind::Deref { arg } => { + parse_by_kind!(self, *arg, _, "does not matter", + @call("mir_make_place", args) => return self.parse_place_inner(args[0]), + _ => (*arg, PlaceElem::Deref), + ) + }, + ExprKind::Index { lhs, index } => (*lhs, PlaceElem::Index(self.parse_local(*index)?)), + ExprKind::Field { lhs, name: field, .. } => (*lhs, PlaceElem::Field(*field, expr.ty)), + _ => { + let place = self.parse_local(expr_id).map(Place::from)?; + return Ok((place, PlaceTy::from_ty(expr.ty))) + }, + ); + let (parent, ty) = self.parse_place_inner(parent)?; + let place = parent.project_deeper(&[proj], self.tcx); + let ty = ty.projection_ty(self.tcx, proj); + Ok((place, ty)) } fn parse_local(&self, expr_id: ExprId) -> PResult { @@ -102,4 +240,16 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { }, ) } + + fn parse_integer_literal(&self, expr_id: ExprId) -> PResult { + parse_by_kind!(self, expr_id, expr, "constant", + ExprKind::Literal { .. } + | ExprKind::NamedConst { .. } + | ExprKind::NonHirLiteral { .. } + | ExprKind::ConstBlock { .. } => Ok({ + let value = as_constant_inner(expr, |_| None, self.tcx); + value.literal.eval_bits(self.tcx, self.param_env, value.ty()) + }), + ) + } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 717c62315..1d96893c7 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -66,14 +66,14 @@ pub fn as_constant_inner<'tcx>( let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty); - Constant { span, user_ty: user_ty, literal } + Constant { span, user_ty, literal } } ExprKind::ZstLiteral { ref user_ty } => { let user_ty = user_ty.as_ref().map(push_cuta).flatten(); let literal = ConstantKind::Val(ConstValue::ZeroSized, ty); - Constant { span, user_ty: user_ty, literal } + Constant { span, user_ty, literal } } ExprKind::NamedConst { def_id, substs, ref user_ty } => { let user_ty = user_ty.as_ref().map(push_cuta).flatten(); @@ -135,14 +135,14 @@ pub(crate) fn lit_to_mir_constant<'tcx>( let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: s.len() } } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: data.len() } } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { let id = tcx.allocate_bytes(data); ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) } diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index c8610af70..c621efb3b 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -27,7 +27,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// suitable also to be passed as function arguments. /// /// The operand returned from this function will *not be valid* after an ExprKind::Scope is - /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an + /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an /// operand suitable for use as a call argument. This is almost always equivalent to /// `as_operand`, except for the particular case of passing values of (potentially) unsized /// types "by value" (see details below). @@ -72,7 +72,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug` /// value to the stack. /// - /// See #68034 for more details. + /// See #68304 for more details. pub(crate) fn as_local_call_operand( &mut self, block: BasicBlock, diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index edd527286..e22fa6365 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -81,8 +81,8 @@ pub(in crate::build) struct PlaceBuilder<'tcx> { /// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be /// part of a path that is captured by a closure. We stop applying projections once we see the first /// projection that isn't captured by a closure. -fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( - mir_projections: &[PlaceElem<'tcx>], +fn convert_to_hir_projections_and_truncate_for_capture( + mir_projections: &[PlaceElem<'_>], ) -> Vec { let mut hir_projections = Vec::new(); let mut variant = None; diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 0814793f2..c7b3eb44d 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -142,7 +142,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let exchange_malloc = Operand::function_handle( tcx, tcx.require_lang_item(LangItem::ExchangeMalloc, Some(expr_span)), - ty::List::empty(), + [], expr_span, ); let storage = this.temp(tcx.mk_mut_ptr(tcx.types.u8), expr_span); diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 218a26e62..38b1fa91d 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -183,7 +183,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { LogicalOp::And => (else_block, shortcircuit_block), LogicalOp::Or => (shortcircuit_block, else_block), }; - let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1); + let term = TerminatorKind::if_(lhs, blocks.0, blocks.1); this.cfg.terminate(block, source_info, term); this.cfg.push_assign_constant( diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 00dbcaeb0..780836851 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -6,10 +6,8 @@ use rustc_middle::thir::*; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds a block of MIR statements to evaluate the THIR `expr`. - /// If the original expression was an AST statement, - /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the - /// span of that statement (including its semicolon, if any). - /// The scope is used if a statement temporary must be dropped. + /// + /// The `statement_scope` is used if a statement temporary must be dropped. pub(crate) fn stmt_expr( &mut self, mut block: BasicBlock, @@ -59,7 +57,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // question raised here -- should we "freeze" the // value of the lhs here? I'm inclined to think not, // since it seems closer to the semantics of the - // overloaded version, which takes `&mut self`. This + // overloaded version, which takes `&mut self`. This // only affects weird things like `x += {x += 1; x}` // -- is that equal to `x + (x + 1)` or `2*(x+1)`? @@ -115,7 +113,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // it is usually better to focus on `the_value` rather // than the entirety of block(s) surrounding it. - let adjusted_span = (|| { + let adjusted_span = if let ExprKind::Block { block } = expr.kind && let Some(tail_ex) = this.thir[block].expr { @@ -137,10 +135,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { tail_result_is_ignored: true, span: expr.span, }); - return Some(expr.span); - } - None - })(); + Some(expr.span) + } else { + None + }; let temp = unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not)); diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 691cbee2c..0961ce11e 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -30,7 +30,6 @@ mod test; mod util; use std::borrow::Borrow; -use std::convert::TryFrom; use std::mem; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -95,7 +94,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let then_block = this.cfg.start_new_block(); let else_block = this.cfg.start_new_block(); - let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block); + let term = TerminatorKind::if_(operand, then_block, else_block); let source_info = this.source_info(expr_span); this.cfg.terminate(block, source_info, term); @@ -1871,7 +1870,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` // let place = Foo::new(); // match place { foo if inspect(foo) - // => feed(foo), ... } + // => feed(foo), ... } // ``` // // will be treated as if it were really something like: @@ -1886,7 +1885,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` // let place = Foo::new(); // match place { ref mut foo if inspect(foo) - // => feed(foo), ... } + // => feed(foo), ... } // ``` // // will be treated as if it were really something like: @@ -2211,7 +2210,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), }; - let local = LocalDecl::<'tcx> { + let local = LocalDecl { mutability, ty: var_ty, user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 58513bde2..ad7a568a2 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -203,7 +203,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.source_info(match_start_span), TerminatorKind::SwitchInt { discr: Operand::Move(discr), - switch_ty: discr_ty, targets: switch_targets, }, ); @@ -221,7 +220,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 0 => (second_bb, first_bb), v => span_bug!(test.span, "expected boolean value but got {:?}", v), }; - TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb) + TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb) } else { // The switch may be inexhaustive so we have a catch all block debug_assert_eq!(options.len() + 1, target_blocks.len()); @@ -232,7 +231,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); TerminatorKind::SwitchInt { discr: Operand::Copy(place), - switch_ty, targets: switch_targets, } }; @@ -378,7 +376,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate( block, source_info, - TerminatorKind::if_(self.tcx, Operand::Move(result), success_block, fail_block), + TerminatorKind::if_(Operand::Move(result), success_block, fail_block), ); } @@ -458,7 +456,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: source_info.span, // FIXME(#54571): This constant comes from user input (a - // constant in a pattern). Are there forms where users can add + // constant in a pattern). Are there forms where users can add // type annotations here? For example, an associated constant? // Need to experiment. user_ty: None, @@ -482,7 +480,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate( eq_block, source_info, - TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block), + TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block), ); } @@ -506,7 +504,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// This is used by the overall `match_candidates` algorithm to structure /// the match as a whole. See `match_candidates` for more details. /// - /// FIXME(#29623). In some cases, we have some tricky choices to make. for + /// FIXME(#29623). In some cases, we have some tricky choices to make. for /// example, if we are testing that `x == 22`, but the candidate is `x @ /// 13..55`, what should we do? In the event that the test is true, we know /// that the candidate applies, but in the event of false, we don't know @@ -553,16 +551,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // FIXME(#29623) we could use PatKind::Range to rule // things out here, in some cases. - ( - &TestKind::SwitchInt { switch_ty: _, ref options }, - &PatKind::Constant { ref value }, - ) if is_switch_ty(match_pair.pattern.ty) => { + (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Constant { value }) + if is_switch_ty(match_pair.pattern.ty) => + { let index = options.get_index_of(value).unwrap(); self.candidate_without_match_pair(match_pair_index, candidate); Some(index) } - (&TestKind::SwitchInt { switch_ty: _, ref options }, &PatKind::Range(ref range)) => { + (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Range(range)) => { let not_contained = self.values_not_contained_in_range(&*range, options).unwrap_or(false); @@ -580,7 +577,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ( &TestKind::Len { len: test_len, op: BinOp::Eq }, - &PatKind::Slice { ref prefix, ref slice, ref suffix }, + PatKind::Slice { prefix, slice, suffix }, ) => { let pat_len = (prefix.len() + suffix.len()) as u64; match (test_len.cmp(&pat_len), slice) { @@ -617,7 +614,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ( &TestKind::Len { len: test_len, op: BinOp::Ge }, - &PatKind::Slice { ref prefix, ref slice, ref suffix }, + PatKind::Slice { prefix, slice, suffix }, ) => { // the test is `$actual_len >= test_len` let pat_len = (prefix.len() + suffix.len()) as u64; @@ -653,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - (&TestKind::Range(ref test), &PatKind::Range(ref pat)) => { + (TestKind::Range(test), PatKind::Range(pat)) => { use std::cmp::Ordering::*; if test == pat { @@ -680,7 +677,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { no_overlap } - (&TestKind::Range(ref range), &PatKind::Constant { value }) => { + (TestKind::Range(range), &PatKind::Constant { value }) => { if let Some(false) = self.const_range_contains(&*range, value) { // `value` is not contained in the testing range, // so `value` can be matched only if this test fails. @@ -840,8 +837,6 @@ fn trait_method<'tcx>( method_name: Symbol, substs: impl IntoIterator>>, ) -> ConstantKind<'tcx> { - let substs = tcx.mk_substs(substs.into_iter().map(Into::into)); - // The unhygienic comparison here is acceptable because this is only // used on known traits. let item = tcx diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index bd435f9ab..cbd494862 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -7,7 +7,6 @@ use rustc_middle::thir::*; use rustc_middle::ty; use rustc_middle::ty::TypeVisitable; use smallvec::SmallVec; -use std::convert::TryInto; impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn field_match_pairs<'pat>( diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 007f3b55e..9daf68a15 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -28,10 +28,10 @@ use rustc_target::spec::abi::Abi; use super::lints; -pub(crate) fn mir_built<'tcx>( - tcx: TyCtxt<'tcx>, +pub(crate) fn mir_built( + tcx: TyCtxt<'_>, def: ty::WithOptConstParam, -) -> &'tcx rustc_data_structures::steal::Steal> { +) -> &rustc_data_structures::steal::Steal> { if let Some(def) = def.try_upgrade(tcx) { return tcx.mir_built(def); } @@ -372,7 +372,7 @@ struct CFG<'tcx> { } rustc_index::newtype_index! { - struct ScopeId { .. } + struct ScopeId {} } #[derive(Debug)] @@ -487,6 +487,7 @@ fn construct_fn<'tcx>( return custom::build_custom_mir( tcx, fn_def.did.to_def_id(), + fn_id, thir, expr, arguments, @@ -624,12 +625,12 @@ fn construct_const<'a, 'tcx>( /// /// This is required because we may still want to run MIR passes on an item /// with type errors, but normal MIR construction can't handle that in general. -fn construct_error<'tcx>( - tcx: TyCtxt<'tcx>, +fn construct_error( + tcx: TyCtxt<'_>, def: LocalDefId, body_owner_kind: hir::BodyOwnerKind, err: ErrorGuaranteed, -) -> Body<'tcx> { +) -> Body<'_> { let span = tcx.def_span(def); let hir_id = tcx.hir().local_def_id_to_hir_id(def); let generator_kind = tcx.generator_kind(def); diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 33f49ffda..591b41633 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -53,7 +53,7 @@ loop { ``` When processing the `let x`, we will add one drop to the scope for -`x`. The break will then insert a drop for `x`. When we process `let +`x`. The break will then insert a drop for `x`. When we process `let y`, we will add another drop (in fact, to a subscope, but let's ignore that for now); any later drops would also drop `y`. @@ -185,7 +185,7 @@ pub(crate) enum BreakableTarget { } rustc_index::newtype_index! { - struct DropIdx { .. } + struct DropIdx {} } const ROOT_NODE: DropIdx = DropIdx::from_u32(0); @@ -757,7 +757,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if self.tcx.sess.opts.unstable_opts.maximal_hir_to_mir_coverage { // Some consumers of rustc need to map MIR locations back to HIR nodes. Currently the // the only part of rustc that tracks MIR -> HIR is the `SourceScopeLocalData::lint_root` - // field that tracks lint levels for MIR locations. Normally the number of source scopes + // field that tracks lint levels for MIR locations. Normally the number of source scopes // is limited to the set of nodes with lint annotations. The -Zmaximal-hir-to-mir-coverage // flag changes this behavior to maximize the number of source scopes, increasing the // granularity of the MIR->HIR mapping. diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index fb1ea9ed3..03a7f2d70 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -1,10 +1,11 @@ use crate::build::ExprCategory; +use crate::errors::*; use rustc_middle::thir::visit::{self, Visitor}; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_middle::mir::BorrowKind; use rustc_middle::thir::*; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::Level; @@ -12,7 +13,6 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; use rustc_span::Span; -use std::borrow::Cow; use std::ops::Bound; struct UnsafetyVisitor<'a, 'tcx> { @@ -46,7 +46,9 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { self.warn_unused_unsafe( hir_id, block_span, - Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")), + Some(UnusedUnsafeEnclosing::Block { + span: self.tcx.sess.source_map().guess_head_span(enclosing_span), + }), ); f(self); } else { @@ -60,7 +62,9 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { hir_id, span, if self.unsafe_op_in_unsafe_fn_allowed() { - self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn")) + self.body_unsafety + .unsafe_fn_sig_span() + .map(|span| UnusedUnsafeEnclosing::Function { span }) } else { None }, @@ -83,30 +87,11 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { } SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {} SafetyContext::UnsafeFn => { - let (description, note) = kind.description_and_note(self.tcx); // unsafe_op_in_unsafe_fn is disallowed - self.tcx.struct_span_lint_hir( - UNSAFE_OP_IN_UNSAFE_FN, - self.hir_context, - span, - format!("{} is unsafe and requires unsafe block (error E0133)", description,), - |lint| lint.span_label(span, kind.simple_description()).note(note), - ) + kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span); } SafetyContext::Safe => { - let (description, note) = kind.description_and_note(self.tcx); - let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" }; - struct_span_err!( - self.tcx.sess, - span, - E0133, - "{} is unsafe and requires unsafe{} block", - description, - fn_sugg, - ) - .span_label(span, kind.simple_description()) - .note(note) - .emit(); + kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed); } } } @@ -115,23 +100,33 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { &self, hir_id: hir::HirId, block_span: Span, - enclosing_unsafe: Option<(Span, &'static str)>, + enclosing_unsafe: Option, ) { let block_span = self.tcx.sess.source_map().guess_head_span(block_span); - let msg = "unnecessary `unsafe` block"; - self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, msg, |lint| { - lint.span_label(block_span, msg); - if let Some((span, kind)) = enclosing_unsafe { - lint.span_label(span, format!("because it's nested under this `unsafe` {}", kind)); - } - lint - }); + self.tcx.emit_spanned_lint( + UNUSED_UNSAFE, + hir_id, + block_span, + UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, + ); } /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool { self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow } + + /// Handle closures/generators/inline-consts, which is unsafecked with their parent body. + fn visit_inner_body(&mut self, def: ty::WithOptConstParam) { + if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) { + let inner_thir = &inner_thir.borrow(); + let hir_context = self.tcx.hir().local_def_id_to_hir_id(def.did); + let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self }; + inner_visitor.visit_expr(&inner_thir[expr]); + // Unsafe blocks can be used in the inner body, make sure to take it into account + self.safety_context = inner_visitor.safety_context; + } + } } // Searches for accesses to layout constrained fields. @@ -408,16 +403,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } else { ty::WithOptConstParam::unknown(closure_id) }; - let (closure_thir, expr) = self.tcx.thir_body(closure_def).unwrap_or_else(|_| { - (self.tcx.alloc_steal_thir(Thir::new()), ExprId::from_u32(0)) - }); - let closure_thir = &closure_thir.borrow(); - let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id); - let mut closure_visitor = - UnsafetyVisitor { thir: closure_thir, hir_context, ..*self }; - closure_visitor.visit_expr(&closure_thir[expr]); - // Unsafe blocks can be used in closures, make sure to take it into account - self.safety_context = closure_visitor.safety_context; + self.visit_inner_body(closure_def); + } + ExprKind::ConstBlock { did, substs: _ } => { + let def_id = did.expect_local(); + self.visit_inner_body(ty::WithOptConstParam::unknown(def_id)); } ExprKind::Field { lhs, .. } => { let lhs = &self.thir[lhs]; @@ -529,94 +519,201 @@ enum UnsafeOpKind { use UnsafeOpKind::*; impl UnsafeOpKind { - pub fn simple_description(&self) -> &'static str { - match self { - CallToUnsafeFunction(..) => "call to unsafe function", - UseOfInlineAssembly => "use of inline assembly", - InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr", - UseOfMutableStatic => "use of mutable static", - UseOfExternStatic => "use of extern static", - DerefOfRawPointer => "dereference of raw pointer", - AccessToUnionField => "access to union field", - MutationOfLayoutConstrainedField => "mutation of layout constrained field", - BorrowOfLayoutConstrainedField => { - "borrow of layout constrained field with interior mutability" - } - CallToFunctionWith(..) => "call to function with `#[target_feature]`", - } - } - - pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) { + pub fn emit_unsafe_op_in_unsafe_fn_lint( + &self, + tcx: TyCtxt<'_>, + hir_id: hir::HirId, + span: Span, + ) { + // FIXME: ideally we would want to trim the def paths, but this is not + // feasible with the current lint emission API (see issue #106126). match self { - CallToUnsafeFunction(did) => ( - if let Some(did) = did { - Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did))) - } else { - Cow::Borrowed(self.simple_description()) + CallToUnsafeFunction(Some(did)) => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { + span, + function: &with_no_trimmed_paths!(tcx.def_path_str(*did)), }, - "consult the function's documentation for information on how to avoid undefined \ - behavior", ), - UseOfInlineAssembly => ( - Cow::Borrowed(self.simple_description()), - "inline assembly is entirely unchecked and can cause undefined behavior", + CallToUnsafeFunction(None) => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span }, ), - InitializingTypeWith => ( - Cow::Borrowed(self.simple_description()), - "initializing a layout restricted type's field with a value outside the valid \ - range is undefined behavior", + UseOfInlineAssembly => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span }, ), - UseOfMutableStatic => ( - Cow::Borrowed(self.simple_description()), - "mutable statics can be mutated by multiple threads: aliasing violations or data \ - races will cause undefined behavior", + InitializingTypeWith => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span }, ), - UseOfExternStatic => ( - Cow::Borrowed(self.simple_description()), - "extern statics are not controlled by the Rust type system: invalid data, \ - aliasing violations or data races will cause undefined behavior", + UseOfMutableStatic => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span }, ), - DerefOfRawPointer => ( - Cow::Borrowed(self.simple_description()), - "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \ - and cause data races: all of these are undefined behavior", + UseOfExternStatic => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span }, ), - AccessToUnionField => ( - Cow::Borrowed(self.simple_description()), - "the field may not be properly initialized: using uninitialized data will cause \ - undefined behavior", + DerefOfRawPointer => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span }, + ), + AccessToUnionField => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span }, ), - MutationOfLayoutConstrainedField => ( - Cow::Borrowed(self.simple_description()), - "mutating layout constrained fields cannot statically be checked for valid values", + MutationOfLayoutConstrainedField => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span }, ), - BorrowOfLayoutConstrainedField => ( - Cow::Borrowed(self.simple_description()), - "references to fields of layout constrained fields lose the constraints. Coupled \ - with interior mutability, the field can be changed to invalid values", + BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span }, ), - CallToFunctionWith(did) => ( - Cow::from(format!( - "call to function `{}` with `#[target_feature]`", - tcx.def_path_str(*did) - )), - "can only be called if the required target features are available", + CallToFunctionWith(did) => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { + span, + function: &with_no_trimmed_paths!(tcx.def_path_str(*did)), + }, ), } } + + pub fn emit_requires_unsafe_err( + &self, + tcx: TyCtxt<'_>, + span: Span, + unsafe_op_in_unsafe_fn_allowed: bool, + ) { + match self { + CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + function: &tcx.def_path_str(*did), + }); + } + CallToUnsafeFunction(Some(did)) => { + tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe { + span, + function: &tcx.def_path_str(*did), + }); + } + CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err( + CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span }, + ); + } + CallToUnsafeFunction(None) => { + tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span }); + } + UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + UseOfInlineAssembly => { + tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span }); + } + InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + InitializingTypeWith => { + tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span }); + } + UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + UseOfMutableStatic => { + tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span }); + } + UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + UseOfExternStatic => { + tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span }); + } + DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + DerefOfRawPointer => { + tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span }); + } + AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + AccessToUnionField => { + tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span }); + } + MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err( + MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + }, + ); + } + MutationOfLayoutConstrainedField => { + tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span }); + } + BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err( + BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }, + ); + } + BorrowOfLayoutConstrainedField => { + tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span }); + } + CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + function: &tcx.def_path_str(*did), + }); + } + CallToFunctionWith(did) => { + tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe { + span, + function: &tcx.def_path_str(*did), + }); + } + } + } } -pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam) { +pub fn check_unsafety(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) { // THIR unsafeck is gated under `-Z thir-unsafeck` if !tcx.sess.opts.unstable_opts.thir_unsafeck { return; } - // Closures are handled by their owner, if it has a body - if tcx.is_closure(def.did.to_def_id()) { - let hir = tcx.hir(); - let owner = hir.enclosing_body_owner(hir.local_def_id_to_hir_id(def.did)); - tcx.ensure().thir_check_unsafety(owner); + // Closures and inline consts are handled by their owner, if it has a body + if tcx.is_typeck_child(def.did.to_def_id()) { return; } @@ -655,7 +752,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { +pub(crate) fn thir_check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { tcx.thir_check_unsafety_for_const_arg(def) } else { @@ -663,8 +760,8 @@ pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { } } -pub(crate) fn thir_check_unsafety_for_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, +pub(crate) fn thir_check_unsafety_for_const_arg( + tcx: TyCtxt<'_>, (did, param_did): (LocalDefId, DefId), ) { check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs new file mode 100644 index 000000000..7f81aef1c --- /dev/null +++ b/compiler/rustc_mir_build/src/errors.rs @@ -0,0 +1,865 @@ +use crate::thir::pattern::deconstruct_pat::DeconstructedPat; +use crate::thir::pattern::MatchCheckCtxt; +use rustc_errors::Handler; +use rustc_errors::{ + error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + IntoDiagnostic, MultiSpan, SubdiagnosticMessage, +}; +use rustc_hir::def::Res; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_middle::thir::Pat; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{symbol::Ident, Span}; + +#[derive(LintDiagnostic)] +#[diag(mir_build_unconditional_recursion)] +#[help] +pub struct UnconditionalRecursion { + #[label] + pub span: Span, + #[label(mir_build_unconditional_recursion_call_site_label)] + pub call_sites: Vec, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> { + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless)] +#[note] +pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)] +pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> { + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_unsafe_fn_requires_unsafe, code = "E0133")] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafe<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_nameless, code = "E0133")] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafeNameless { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_inline_assembly_requires_unsafe, code = "E0133")] +#[note] +pub struct UseOfInlineAssemblyRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_initializing_type_with_requires_unsafe, code = "E0133")] +#[note] +pub struct InitializingTypeWithRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_mutable_static_requires_unsafe, code = "E0133")] +#[note] +pub struct UseOfMutableStaticRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_extern_static_requires_unsafe, code = "E0133")] +#[note] +pub struct UseOfExternStaticRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_deref_raw_pointer_requires_unsafe, code = "E0133")] +#[note] +pub struct DerefOfRawPointerRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_union_field_requires_unsafe, code = "E0133")] +#[note] +pub struct AccessToUnionFieldRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_mutation_of_layout_constrained_field_requires_unsafe, code = "E0133")] +#[note] +pub struct MutationOfLayoutConstrainedFieldRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_borrow_of_layout_constrained_field_requires_unsafe, code = "E0133")] +#[note] +pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_fn_with_requires_unsafe, code = "E0133")] +#[note] +pub struct CallToFunctionWithRequiresUnsafe<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unused_unsafe)] +pub struct UnusedUnsafe { + #[label] + pub span: Span, + #[subdiagnostic] + pub enclosing: Option, +} + +#[derive(Subdiagnostic)] +pub enum UnusedUnsafeEnclosing { + #[label(mir_build_unused_unsafe_enclosing_block_label)] + Block { + #[primary_span] + span: Span, + }, + #[label(mir_build_unused_unsafe_enclosing_fn_label)] + Function { + #[primary_span] + span: Span, + }, +} + +pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { + pub cx: &'m MatchCheckCtxt<'p, 'tcx>, + pub expr_span: Span, + pub span: Span, + pub ty: Ty<'tcx>, +} + +impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { + fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::mir_build_non_exhaustive_patterns_type_not_empty, + error_code!(E0004), + ); + + let peeled_ty = self.ty.peel_refs(); + diag.set_arg("ty", self.ty); + diag.set_arg("peeled_ty", peeled_ty); + + if let ty::Adt(def, _) = peeled_ty.kind() { + let def_span = self + .cx + .tcx + .hir() + .get_if_local(def.did()) + .and_then(|node| node.ident()) + .map(|ident| ident.span) + .unwrap_or_else(|| self.cx.tcx.def_span(def.did())); + + // workaround to make test pass + let mut span: MultiSpan = def_span.into(); + span.push_span_label(def_span, ""); + + diag.span_note(span, rustc_errors::fluent::def_note); + } + + let is_variant_list_non_exhaustive = match self.ty.kind() { + ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => { + true + } + _ => false, + }; + + if is_variant_list_non_exhaustive { + diag.note(rustc_errors::fluent::non_exhaustive_type_note); + } else { + diag.note(rustc_errors::fluent::type_note); + } + + if let ty::Ref(_, sub_ty, _) = self.ty.kind() { + if !sub_ty.is_inhabited_from(self.cx.tcx, self.cx.module, self.cx.param_env) { + diag.note(rustc_errors::fluent::reference_note); + } + } + + let mut suggestion = None; + let sm = self.cx.tcx.sess.source_map(); + if self.span.eq_ctxt(self.expr_span) { + // Get the span for the empty match body `{}`. + let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) { + (format!("\n{}", snippet), " ") + } else { + (" ".to_string(), "") + }; + suggestion = Some(( + self.span.shrink_to_hi().with_hi(self.expr_span.hi()), + format!( + " {{{indentation}{more}_ => todo!(),{indentation}}}", + indentation = indentation, + more = more, + ), + )); + } + + if let Some((span, sugg)) = suggestion { + diag.span_suggestion_verbose( + span, + rustc_errors::fluent::suggestion, + sugg, + Applicability::HasPlaceholders, + ); + } else { + diag.help(rustc_errors::fluent::help); + } + + diag + } +} + +#[derive(Diagnostic)] +#[diag(mir_build_static_in_pattern, code = "E0158")] +pub struct StaticInPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_assoc_const_in_pattern, code = "E0158")] +pub struct AssocConstInPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_const_param_in_pattern, code = "E0158")] +pub struct ConstParamInPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_non_const_path, code = "E0080")] +pub struct NonConstPath { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unreachable_pattern)] +pub struct UnreachablePattern { + #[label] + pub span: Option, + #[label(catchall_label)] + pub catchall: Option, +} + +#[derive(Diagnostic)] +#[diag(mir_build_const_pattern_depends_on_generic_parameter)] +pub struct ConstPatternDependsOnGenericParameter { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_could_not_eval_const_pattern)] +pub struct CouldNotEvalConstPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper, code = "E0030")] +pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper { + #[primary_span] + #[label] + pub span: Span, + #[note(teach_note)] + pub teach: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_literal_in_range_out_of_bounds)] +pub struct LiteralOutOfRange<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'tcx>, + pub max: u128, +} + +#[derive(Diagnostic)] +#[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")] +pub struct LowerRangeBoundMustBeLessThanUpper { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_leading_irrefutable_let_patterns)] +#[note] +#[help] +pub struct LeadingIrrefutableLetPatterns { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_trailing_irrefutable_let_patterns)] +#[note] +#[help] +pub struct TrailingIrrefutableLetPatterns { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_bindings_with_variant_name, code = "E0170")] +pub struct BindingsWithVariantName { + #[suggestion(code = "{ty_path}::{ident}", applicability = "machine-applicable")] + pub suggestion: Option, + pub ty_path: String, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_generic_let)] +#[note] +#[help] +pub struct IrrefutableLetPatternsGenericLet { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_if_let)] +#[note] +#[help] +pub struct IrrefutableLetPatternsIfLet { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_if_let_guard)] +#[note] +#[help] +pub struct IrrefutableLetPatternsIfLetGuard { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_let_else)] +#[note] +#[help] +pub struct IrrefutableLetPatternsLetElse { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_while_let)] +#[note] +#[help] +pub struct IrrefutableLetPatternsWhileLet { + pub count: usize, +} + +#[derive(Diagnostic)] +#[diag(mir_build_borrow_of_moved_value)] +pub struct BorrowOfMovedValue<'tcx> { + #[primary_span] + pub span: Span, + #[label] + #[label(occurs_because_label)] + pub binding_span: Span, + #[label(value_borrowed_label)] + pub conflicts_ref: Vec, + pub name: Ident, + pub ty: Ty<'tcx>, + #[suggestion(code = "ref ", applicability = "machine-applicable")] + pub suggest_borrowing: Option, +} + +#[derive(Diagnostic)] +#[diag(mir_build_multiple_mut_borrows)] +pub struct MultipleMutBorrows { + #[primary_span] + pub span: Span, + #[label] + pub binding_span: Span, + #[subdiagnostic] + pub occurences: Vec, + pub name: Ident, +} + +#[derive(Subdiagnostic)] +pub enum MultipleMutBorrowOccurence { + #[label(mutable_borrow)] + Mutable { + #[primary_span] + span: Span, + name_mut: Ident, + }, + #[label(immutable_borrow)] + Immutable { + #[primary_span] + span: Span, + name_immut: Ident, + }, + #[label(moved)] + Moved { + #[primary_span] + span: Span, + name_moved: Ident, + }, +} + +#[derive(Diagnostic)] +#[diag(mir_build_union_pattern)] +pub struct UnionPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_type_not_structural)] +pub struct TypeNotStructural<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_invalid_pattern)] +pub struct InvalidPattern<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_unsized_pattern)] +pub struct UnsizedPattern<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_float_pattern)] +pub struct FloatPattern; + +#[derive(LintDiagnostic)] +#[diag(mir_build_pointer_pattern)] +pub struct PointerPattern; + +#[derive(LintDiagnostic)] +#[diag(mir_build_indirect_structural_match)] +pub struct IndirectStructuralMatch<'tcx> { + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_nontrivial_structural_match)] +pub struct NontrivialStructuralMatch<'tcx> { + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_overlapping_range_endpoints)] +#[note] +pub struct OverlappingRangeEndpoints<'tcx> { + #[label(range)] + pub range: Span, + #[subdiagnostic] + pub overlap: Vec>, +} + +pub struct Overlap<'tcx> { + pub span: Span, + pub range: Pat<'tcx>, +} + +impl<'tcx> AddToDiagnostic for Overlap<'tcx> { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let Overlap { span, range } = self; + + // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` + // does not support `#[subdiagnostic(eager)]`... + let message = format!("this range overlaps on `{range}`..."); + diag.span_label(span, message); + } +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_non_exhaustive_omitted_pattern)] +#[help] +#[note] +pub(crate) struct NonExhaustiveOmittedPattern<'tcx> { + pub scrut_ty: Ty<'tcx>, + #[subdiagnostic] + pub uncovered: Uncovered<'tcx>, +} + +#[derive(Subdiagnostic)] +#[label(mir_build_uncovered)] +pub(crate) struct Uncovered<'tcx> { + #[primary_span] + span: Span, + count: usize, + witness_1: Pat<'tcx>, + witness_2: Pat<'tcx>, + witness_3: Pat<'tcx>, + remainder: usize, +} + +impl<'tcx> Uncovered<'tcx> { + pub fn new<'p>( + span: Span, + cx: &MatchCheckCtxt<'p, 'tcx>, + witnesses: Vec>, + ) -> Self { + let witness_1 = witnesses.get(0).unwrap().to_pat(cx); + Self { + span, + count: witnesses.len(), + // Substitute dummy values if witnesses is smaller than 3. These will never be read. + witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_1, + remainder: witnesses.len().saturating_sub(3), + } + } +} + +#[derive(Diagnostic)] +#[diag(mir_build_pattern_not_covered, code = "E0005")] +pub(crate) struct PatternNotCovered<'s, 'tcx> { + #[primary_span] + pub span: Span, + pub origin: &'s str, + #[subdiagnostic] + pub uncovered: Uncovered<'tcx>, + #[subdiagnostic] + pub inform: Option, + #[subdiagnostic] + pub interpreted_as_const: Option, + #[subdiagnostic] + pub adt_defined_here: Option>, + #[note(pattern_ty)] + pub _p: (), + pub pattern_ty: Ty<'tcx>, + #[subdiagnostic] + pub let_suggestion: Option, + #[subdiagnostic] + pub misc_suggestion: Option, + #[subdiagnostic] + pub res_defined_here: Option, +} + +#[derive(Subdiagnostic)] +#[note(mir_build_inform_irrefutable)] +#[note(mir_build_more_information)] +pub struct Inform; + +pub struct AdtDefinedHere<'tcx> { + pub adt_def_span: Span, + pub ty: Ty<'tcx>, + pub variants: Vec, +} + +pub struct Variant { + pub span: Span, +} + +impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.set_arg("ty", self.ty); + let mut spans = MultiSpan::from(self.adt_def_span); + + for Variant { span } in self.variants { + spans.push_span_label(span, rustc_errors::fluent::mir_build_variant_defined_here); + } + + diag.span_note(spans, rustc_errors::fluent::mir_build_adt_defined_here); + } +} + +#[derive(Subdiagnostic)] +#[label(mir_build_res_defined_here)] +pub struct ResDefinedHere { + #[primary_span] + pub def_span: Span, + pub res: Res, +} + +#[derive(Subdiagnostic)] +#[suggestion( + mir_build_interpreted_as_const, + code = "{variable}_var", + applicability = "maybe-incorrect" +)] +#[label(mir_build_confused)] +pub struct InterpretedAsConst { + #[primary_span] + pub span: Span, + pub article: &'static str, + pub variable: String, + pub res: Res, +} + +#[derive(Subdiagnostic)] +pub enum SuggestLet { + #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] + If { + #[suggestion_part(code = "if ")] + start_span: Span, + #[suggestion_part(code = " {{ todo!() }}")] + semi_span: Span, + count: usize, + }, + #[suggestion( + mir_build_suggest_let_else, + code = " else {{ todo!() }}", + applicability = "has-placeholders" + )] + Else { + #[primary_span] + end_span: Span, + count: usize, + }, +} + +#[derive(Subdiagnostic)] +pub enum MiscPatternSuggestion { + #[suggestion( + mir_build_suggest_attempted_int_lit, + code = "_", + applicability = "maybe-incorrect" + )] + AttemptedIntegerLiteral { + #[primary_span] + start_span: Span, + }, +} diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 879752945..a428180a4 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -5,11 +5,11 @@ #![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(box_patterns)] -#![feature(control_flow_enum)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(min_specialization)] #![feature(once_cell)] +#![feature(try_blocks)] #![recursion_limit = "256"] #[macro_use] @@ -19,6 +19,7 @@ extern crate rustc_middle; mod build; mod check_unsafety; +mod errors; mod lints; pub mod thir; diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index b21f30efc..f67f24b43 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -1,3 +1,4 @@ +use crate::errors::UnconditionalRecursion; use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; @@ -36,19 +37,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let sp = tcx.def_span(def_id); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( UNCONDITIONAL_RECURSION, hir_id, sp, - "function cannot return without recursing", - |lint| { - lint.span_label(sp, "cannot return without recursing"); - // offer some help to the programmer. - for call_span in vis.reachable_recursive_calls { - lint.span_label(call_span, "recursive call site"); - } - lint.help("a `loop` may express intention better if this is on purpose") - }, + UnconditionalRecursion { span: sp, call_sites: vis.reachable_recursive_calls }, ); } } @@ -67,7 +60,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> { /// Returns `true` if `func` refers to the function we are searching in. fn is_recursive_call(&self, func: &Operand<'tcx>, args: &[Operand<'tcx>]) -> bool { let Search { tcx, body, trait_substs, .. } = *self; - // Resolving function type to a specific instance that is being called is expensive. To + // Resolving function type to a specific instance that is being called is expensive. To // avoid the cost we check the number of arguments first, which is sufficient to reject // most of calls as non-recursive. if args.len() != body.arg_count { @@ -125,7 +118,7 @@ impl<'mir, 'tcx> TriColorVisitor> for Search<'mir, 'tcx> { // A diverging InlineAsm is treated as non-recursing TerminatorKind::InlineAsm { destination, .. } => { if destination.is_some() { - ControlFlow::CONTINUE + ControlFlow::Continue(()) } else { ControlFlow::Break(NonRecursive) } @@ -139,7 +132,7 @@ impl<'mir, 'tcx> TriColorVisitor> for Search<'mir, 'tcx> { | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE, + | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()), } } @@ -152,7 +145,7 @@ impl<'mir, 'tcx> TriColorVisitor> for Search<'mir, 'tcx> { } } - ControlFlow::CONTINUE + ControlFlow::Continue(()) } fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index a9ed945d4..57ae6a365 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -33,13 +33,13 @@ pub(crate) fn lit_to_const<'tcx>( let str_bytes = s.as_str().as_bytes(); ty::ValTree::from_raw_bytes(tcx, str_bytes) } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index b5c4b7b13..a355e1bda 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -18,10 +18,10 @@ use rustc_middle::thir::*; use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; use rustc_span::Span; -pub(crate) fn thir_body<'tcx>( - tcx: TyCtxt<'tcx>, +pub(crate) fn thir_body( + tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam, -) -> Result<(&'tcx Steal>, ExprId), ErrorGuaranteed> { +) -> Result<(&Steal>, ExprId), ErrorGuaranteed> { let hir = tcx.hir(); let body = hir.body(hir.body_owned_by(owner_def.did)); let mut cx = Cx::new(tcx, owner_def); @@ -52,10 +52,7 @@ pub(crate) fn thir_body<'tcx>( Ok((tcx.alloc_steal_thir(cx.thir), expr)) } -pub(crate) fn thir_tree<'tcx>( - tcx: TyCtxt<'tcx>, - owner_def: ty::WithOptConstParam, -) -> String { +pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam) -> String { match thir_body(tcx, owner_def) { Ok((thir, _)) => format!("{:#?}", thir.steal()), Err(_) => "error".into(), diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e369dba55..34e637f59 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -4,18 +4,22 @@ use super::usefulness::{ }; use super::{PatCtxt, PatternError}; +use crate::errors::*; + +use hir::{ExprKind, PatKind}; use rustc_arena::TypedArena; -use rustc_ast::Mutability; +use rustc_ast::{LitKind, Mutability}; use rustc_errors::{ - error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{HirId, Pat}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; + use rustc_session::lint::builtin::{ BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, }; @@ -107,28 +111,20 @@ impl PatCtxt<'_, '_> { for error in &self.errors { match *error { PatternError::StaticInPattern(span) => { - self.span_e0158(span, "statics cannot be referenced in patterns") + self.tcx.sess.emit_err(StaticInPattern { span }); } PatternError::AssocConstInPattern(span) => { - self.span_e0158(span, "associated consts cannot be referenced in patterns") + self.tcx.sess.emit_err(AssocConstInPattern { span }); } PatternError::ConstParamInPattern(span) => { - self.span_e0158(span, "const parameters cannot be referenced in patterns") + self.tcx.sess.emit_err(ConstParamInPattern { span }); } PatternError::NonConstPath(span) => { - rustc_middle::mir::interpret::struct_error( - self.tcx.at(span), - "runtime values cannot be referenced in patterns", - ) - .emit(); + self.tcx.sess.emit_err(NonConstPath { span }); } } } } - - fn span_e0158(&self, span: Span, text: &str) { - struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit(); - } } impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { @@ -251,14 +247,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId) -> bool { let hir = self.tcx.hir(); - let parent = hir.get_parent_node(pat_id); + let parent = hir.parent_id(pat_id); // First, figure out if the given pattern is part of a let chain, // and if so, obtain the top node of the chain. let mut top = parent; let mut part_of_chain = false; loop { - let new_top = hir.get_parent_node(top); + let new_top = hir.parent_id(top); if let hir::Node::Expr( hir::Expr { kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs), @@ -345,29 +341,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { ); return true; } - let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| { - let span_start = affix[0].unwrap().0; - let span_end = affix.last().unwrap().unwrap().0; - let span = span_start.to(span_end); - let cnt = affix.len(); - let s = pluralize!(cnt); - cx.tcx.struct_span_lint_hir( - IRREFUTABLE_LET_PATTERNS, - top, - span, - format!("{kind} irrefutable pattern{s} in let chain"), - |lint| { - lint.note(format!( - "{these} pattern{s} will always match", - these = pluralize!("this", cnt), - )) - .help(format!( - "consider moving {} {suggestion}", - if cnt > 1 { "them" } else { "it" } - )) - }, - ); - }; if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 { // The chain has a non-zero prefix of irrefutable `let` statements. @@ -381,13 +354,21 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) { // Emit the lint let prefix = &chain_refutabilities[..until]; - lint_affix(prefix, "leading", "outside of the construct"); + let span_start = prefix[0].unwrap().0; + let span_end = prefix.last().unwrap().unwrap().0; + let span = span_start.to(span_end); + let count = prefix.len(); + cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, LeadingIrrefutableLetPatterns { count }); } } if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) { // The chain has a non-empty suffix of irrefutable `let` statements let suffix = &chain_refutabilities[from + 1..]; - lint_affix(suffix, "trailing", "into the body"); + let span_start = suffix[0].unwrap().0; + let span_end = suffix.last().unwrap().unwrap().0; + let span = span_start.to(span_end); + let count = suffix.len(); + cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, TrailingIrrefutableLetPatterns { count }); } true } @@ -397,8 +378,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let pattern = self.lower_pattern(&mut cx, pat, &mut false); let pattern_ty = pattern.ty(); - let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }; + let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -409,145 +390,84 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { return; } - let joined_patterns = joined_uncovered_patterns(&cx, &witnesses); - - let mut bindings = vec![]; - - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0005, - "refutable pattern in {}: {} not covered", - origin, - joined_patterns - ); - let suggest_if_let = match &pat.kind { - hir::PatKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].args.is_none() => + let (inform, interpreted_as_const, res_defined_here,let_suggestion, misc_suggestion) = + if let hir::PatKind::Path(hir::QPath::Resolved( + None, + hir::Path { + segments: &[hir::PathSegment { args: None, res, ident, .. }], + .. + }, + )) = &pat.kind { - const_not_var(&mut err, cx.tcx, pat, path); - false - } - _ => { - pat.walk(&mut |pat: &hir::Pat<'_>| { - match pat.kind { - hir::PatKind::Binding(_, _, ident, _) => { - bindings.push(ident); + ( + None, + Some(InterpretedAsConst { + span: pat.span, + article: res.article(), + variable: ident.to_string().to_lowercase(), + res, + }), + try { + ResDefinedHere { + def_span: cx.tcx.hir().res_span(res)?, + res, } - _ => {} + }, + None, + None, + ) + } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) { + let mut bindings = vec![]; + pat.walk_always(&mut |pat: &hir::Pat<'_>| { + if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { + bindings.push(ident); } - true }); - - err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns)); - true - } - }; - - if let (Some(span), true) = (sp, suggest_if_let) { - err.note( - "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ - an `enum` with only one variant", - ); - if self.tcx.sess.source_map().is_span_accessible(span) { let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1)); let start_span = span.shrink_to_lo(); let end_span = semi_span.shrink_to_lo(); - err.multipart_suggestion( - &format!( - "you might want to use `if let` to ignore the variant{} that {} matched", - pluralize!(witnesses.len()), - match witnesses.len() { - 1 => "isn't", - _ => "aren't", - }, - ), - vec![ - match &bindings[..] { - [] => (start_span, "if ".to_string()), - [binding] => (start_span, format!("let {} = if ", binding)), - bindings => ( - start_span, - format!( - "let ({}) = if ", - bindings - .iter() - .map(|ident| ident.to_string()) - .collect::>() - .join(", ") - ), - ), - }, - match &bindings[..] { - [] => (semi_span, " { todo!() }".to_string()), - [binding] => { - (end_span, format!(" {{ {} }} else {{ todo!() }}", binding)) - } - bindings => ( - end_span, - format!( - " {{ ({}) }} else {{ todo!() }}", - bindings - .iter() - .map(|ident| ident.to_string()) - .collect::>() - .join(", ") - ), - ), - }, - ], - Applicability::HasPlaceholders, - ); - if !bindings.is_empty() { - err.span_suggestion_verbose( - semi_span.shrink_to_lo(), - &format!( - "alternatively, you might want to use \ - let else to handle the variant{} that {} matched", - pluralize!(witnesses.len()), - match witnesses.len() { - 1 => "isn't", - _ => "aren't", - }, - ), - " else { todo!() }", - Applicability::HasPlaceholders, - ); - } - } - err.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-02-refutability.html", - ); - } - - adt_defined_here(&cx, &mut err, pattern_ty, &witnesses); - err.note(&format!("the matched value is of type `{}`", pattern_ty)); - err.emit(); - } -} + let count = witnesses.len(); + + // If the pattern to match is an integer literal: + let int_suggestion = if + let PatKind::Lit(expr) = &pat.kind + && bindings.is_empty() + && let ExprKind::Lit(Spanned { node: LitKind::Int(_, _), span }) = expr.kind { + // Then give a suggestion, the user might've meant to create a binding instead. + Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: span.shrink_to_lo() }) + } else { None }; + + let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }}; + (sp.map(|_|Inform), None, None, Some(let_suggestion), int_suggestion) + } else{ + (sp.map(|_|Inform), None, None, None, None) + }; -/// A path pattern was interpreted as a constant, not a new variable. -/// This caused an irrefutable match failure in e.g. `let`. -fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) { - let descr = path.res.descr(); - err.span_label( - pat.span, - format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,), - ); + let adt_defined_here = try { + let ty = pattern_ty.peel_refs(); + let ty::Adt(def, _) = ty.kind() else { None? }; + let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span; + let mut variants = vec![]; - err.span_suggestion( - pat.span, - "introduce a variable instead", - format!("{}_var", path.segments[0].ident).to_lowercase(), - // Cannot use `MachineApplicable` as it's not really *always* correct - // because there may be such an identifier in scope or the user maybe - // really wanted to match against the constant. This is quite unlikely however. - Applicability::MaybeIncorrect, - ); + for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) { + variants.push(Variant { span }); + } + AdtDefinedHere { adt_def_span, ty, variants } + }; - if let Some(span) = tcx.hir().res_span(path.res) { - err.span_label(span, format!("{} defined here", descr)); + self.tcx.sess.emit_err(PatternNotCovered { + span: pat.span, + origin, + uncovered: Uncovered::new(pat.span, &cx, witnesses), + inform, + interpreted_as_const, + _p: (), + pattern_ty, + let_suggestion, + misc_suggestion, + res_defined_here, + adt_defined_here, + }); } } @@ -568,32 +488,22 @@ fn check_for_bindings_named_same_as_variants( }) { let variant_count = edef.variants().len(); - cx.tcx.struct_span_lint_hir( + let ty_path = with_no_trimmed_paths!({ + cx.tcx.def_path_str(edef.did()) + }); + cx.tcx.emit_spanned_lint( BINDINGS_WITH_VARIANT_NAME, p.hir_id, p.span, - DelayDm(|| format!( - "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", - ident, cx.tcx.def_path_str(edef.did()) - )), - |lint| { - let ty_path = cx.tcx.def_path_str(edef.did()); - lint.code(error_code!(E0170)); - + BindingsWithVariantName { // If this is an irrefutable pattern, and there's > 1 variant, // then we can't actually match on this. Applying the below // suggestion would produce code that breaks on `check_irrefutable`. - if rf == Refutable || variant_count == 1 { - lint.span_suggestion( - p.span, - "to match on the variant, qualify the path", - format!("{}::{}", ty_path, ident), - Applicability::MachineApplicable, - ); - } - - lint + suggestion: if rf == Refutable || variant_count == 1 { + Some(p.span) + } else { None }, + ty_path, + ident, }, ) } @@ -611,14 +521,12 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { } fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option) { - tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| { - if let Some(catchall) = catchall { - // We had a catchall pattern, hint at that. - lint.span_label(span, "unreachable pattern"); - lint.span_label(catchall, "matches any value"); - } - lint - }); + tcx.emit_spanned_lint( + UNREACHABLE_PATTERNS, + id, + span, + UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall }, + ); } fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) { @@ -634,67 +542,18 @@ fn irrefutable_let_patterns( span: Span, ) { macro_rules! emit_diag { - ( - $lint:expr, - $source_name:expr, - $note_sufix:expr, - $help_sufix:expr - ) => {{ - let s = pluralize!(count); - let these = pluralize!("this", count); - tcx.struct_span_lint_hir( - IRREFUTABLE_LET_PATTERNS, - id, - span, - format!("irrefutable {} pattern{s}", $source_name), - |lint| { - lint.note(&format!( - "{these} pattern{s} will always match, so the {}", - $note_sufix - )) - .help(concat!("consider ", $help_sufix)) - }, - ) + ($lint:tt) => {{ + tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count }); }}; } match source { - LetSource::GenericLet => { - emit_diag!(lint, "`let`", "`let` is useless", "removing `let`"); - } - LetSource::IfLet => { - emit_diag!( - lint, - "`if let`", - "`if let` is useless", - "replacing the `if let` with a `let`" - ); - } - LetSource::IfLetGuard => { - emit_diag!( - lint, - "`if let` guard", - "guard is useless", - "removing the guard and adding a `let` inside the match arm" - ); - } - LetSource::LetElse => { - emit_diag!( - lint, - "`let...else`", - "`else` clause is useless", - "removing the `else` clause" - ); - } - LetSource::WhileLet => { - emit_diag!( - lint, - "`while let`", - "loop will never exit", - "instead using a `loop { ... }` with a `let` inside it" - ); - } - }; + LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet), + LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet), + LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard), + LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse), + LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet), + } } fn is_let_irrefutable<'p, 'tcx>( @@ -760,15 +619,17 @@ fn non_exhaustive_match<'p, 'tcx>( // informative. let mut err; let pattern; - let mut patterns_len = 0; + let patterns_len; if is_empty_match && !non_empty_enum { - err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), - ); - pattern = "_".to_string(); + cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty { + cx, + expr_span, + span: sp, + ty: scrut_ty, + }); + return; } else { + // FIXME: migration of this diagnostic will require list support let joined_patterns = joined_uncovered_patterns(cx, &witnesses); err = create_e0004( cx.tcx.sess, @@ -1039,24 +900,17 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa } }); if !conflicts_ref.is_empty() { - let occurs_because = format!( - "move occurs because `{}` has type `{}` which does not implement the `Copy` trait", + sess.emit_err(BorrowOfMovedValue { + span: pat.span, + binding_span, + conflicts_ref, name, - typeck_results.node_type(pat.hir_id), - ); - let mut err = sess.struct_span_err(pat.span, "borrow of moved value"); - err.span_label(binding_span, format!("value moved into `{}` here", name)) - .span_label(binding_span, occurs_because) - .span_labels(conflicts_ref, "value borrowed here after move"); - if pat.span.contains(binding_span) { - err.span_suggestion_verbose( - binding_span.shrink_to_lo(), - "borrow this binding in the pattern to avoid moving the value", - "ref ".to_string(), - Applicability::MachineApplicable, - ); - } - err.emit(); + ty: typeck_results.node_type(pat.hir_id), + suggest_borrowing: pat + .span + .contains(binding_span) + .then(|| binding_span.shrink_to_lo()), + }); } return; } @@ -1086,19 +940,18 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa // Report errors if any. if !conflicts_mut_mut.is_empty() { // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`. - let mut err = sess - .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time"); - err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name)); - for (span, name) in conflicts_mut_mut { - err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name)); + let mut occurences = vec![]; + + for (span, name_mut) in conflicts_mut_mut { + occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut }); } - for (span, name) in conflicts_mut_ref { - err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name)); + for (span, name_immut) in conflicts_mut_ref { + occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut }); } - for (span, name) in conflicts_move { - err.span_label(span, format!("also moved into `{}` here", name)); + for (span, name_moved) in conflicts_move { + occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved }); } - err.emit(); + sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name }); } else if !conflicts_mut_ref.is_empty() { // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse. let (primary, also) = match mut_outer { @@ -1140,7 +993,7 @@ pub enum LetSource { fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { let hir = tcx.hir(); - let parent = hir.get_parent_node(pat_id); + let parent = hir.parent_id(pat_id); let_source_parent(tcx, parent, Some(pat_id)) } @@ -1159,7 +1012,7 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option) -> L _ => {} } - let parent_parent = hir.get_parent_node(parent); + let parent_parent = hir.parent_id(parent); let parent_parent_node = hir.get(parent_parent); match parent_parent_node { hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), .. }) => { @@ -1171,8 +1024,8 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option) -> L _ => {} } - let parent_parent_parent = hir.get_parent_node(parent_parent); - let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); + let parent_parent_parent = hir.parent_id(parent_parent); + let parent_parent_parent_parent = hir.parent_id(parent_parent_parent); let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent); if let hir::Node::Expr(hir::Expr { diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a21f6cd39..7f3519945 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,11 +1,9 @@ -use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; use std::cell::Cell; use super::PatCtxt; +use crate::errors::{ + FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch, + PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, +}; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). @@ -70,7 +72,7 @@ mod fallback_to_const_ref { /// hoops to get a reference to the value. pub(super) struct FallbackToConstRef(()); - pub(super) fn fallback_to_const_ref<'tcx>(c2p: &super::ConstToPat<'tcx>) -> FallbackToConstRef { + pub(super) fn fallback_to_const_ref(c2p: &super::ConstToPat<'_>) -> FallbackToConstRef { assert!(c2p.behind_reference.get()); FallbackToConstRef(()) } @@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> { self.infcx.tcx } - fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String { - let path = self.tcx().def_path_str(adt_def.did()); - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ) - } - - fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind() { - ty::Adt(adt, _) => self.adt_derive_msg(*adt), - ty::Dynamic(..) => { - "trait objects cannot be used in patterns".to_string() - } - ty::Opaque(..) => { - "opaque types cannot be used in patterns".to_string() - } - ty::Closure(..) => { - "closures cannot be used in patterns".to_string() - } - ty::Generator(..) | ty::GeneratorWitness(..) => { - "generators cannot be used in patterns".to_string() - } - ty::Float(..) => { - "floating-point numbers cannot be used in patterns".to_string() - } - ty::FnPtr(..) => { - "function pointers cannot be used in patterns".to_string() - } - ty::RawPtr(..) => { - "raw pointers cannot be used in patterns".to_string() - } - _ => { - bug!("use of a value of `{non_sm_ty}` inside a pattern") - } - }) - }) - } - fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { ty.is_structural_eq_shallow(self.infcx.tcx) } @@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> { // If we were able to successfully convert the const to some pat, // double-check that all types in the const implement `Structural`. - let structural = self.search_for_structural_match_violation(cv.ty()); + let structural = + traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); debug!( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty(), @@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> { return inlined_const_as_pat; } - if let Some(msg) = structural { + if let Some(non_sm_ty) = structural { if !self.type_may_have_partial_eq_impl(cv.ty()) { - // span_fatal avoids ICE from resolution of non-existent method (rare case). - self.tcx().sess.span_fatal(self.span, &msg); + // fatal avoids ICE from resolution of non-existent method (rare case). + self.tcx() + .sess + .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty }); } else if mir_structural_match_violation && !self.saw_const_match_lint.get() { - self.tcx().struct_span_lint_hir( + self.tcx().emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - msg, - |lint| lint, + IndirectStructuralMatch { non_sm_ty }, ); } else { debug!( @@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> { let kind = match cv.ty().kind() { ty::Float(_) => { if self.include_lint_checks { - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, - "floating-point types cannot be used in patterns", - |lint| lint, + FloatPattern, ); } PatKind::Constant { value: cv } @@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> { ty::Adt(adt_def, _) if adt_def.is_union() => { // Matching on union fields is unsafe, we can't hide it in constants self.saw_const_match_error.set(true); - let msg = "cannot use unions in constant patterns"; - if self.include_lint_checks { - tcx.sess.span_err(span, msg); - } else { - tcx.sess.delay_span_bug(span, msg); - } + let err = UnionPattern { span }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } ty::Adt(..) if !self.type_may_have_partial_eq_impl(cv.ty()) // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty()).is_some() => + + // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we + // could get `Option`, even though `Option` is annotated with derive. + && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) => { - // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we - // could get `Option`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty()).unwrap(); self.saw_const_match_error.set(true); - if self.include_lint_checks { - tcx.sess.span_err(self.span, &msg); - } else { - tcx.sess.delay_span_bug(self.span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } // If the type is not structurally comparable, just emit the constant directly, @@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> { && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, - DelayDm(|| { - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - cv.ty(), - cv.ty(), - ) - }), - |lint| lint, + IndirectStructuralMatch { non_sm_ty: cv.ty() }, ); } // Since we are behind a reference, we can just bubble the error up so we get a @@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> { adt_def, cv.ty() ); - let path = tcx.def_path_str(adt_def.did()); - let msg = format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ); self.saw_const_match_error.set(true); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } ty::Adt(adt_def, substs) if adt_def.is_enum() => { @@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> { // These are not allowed and will error elsewhere anyway. ty::Dynamic(..) => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty()); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = InvalidPattern { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } // `&str` is represented as `ConstValue::Slice`, let's keep using this @@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> { // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a // reference. This makes the rest of the matching logic simpler as it doesn't have // to figure out how to get a reference again. - ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => { + ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { if self.behind_reference.get() { if self.include_lint_checks && !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() { - self.saw_const_match_lint.set(true); - let msg = self.adt_derive_msg(adt_def); - self.tcx().struct_span_lint_hir( + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, - self.span, - msg, - |lint| lint, + span, + IndirectStructuralMatch { non_sm_ty: *pointee_ty }, ); } PatKind::Constant { value: cv } } else { if !self.saw_const_match_error.get() { self.saw_const_match_error.set(true); - let msg = self.adt_derive_msg(adt_def); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty: *pointee_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); } PatKind::Wild } @@ -508,12 +436,10 @@ impl<'tcx> ConstToPat<'tcx> { if !pointee_ty.is_sized(tcx, param_env) { // `tcx.deref_mir_constant()` below will ICE with an unsized type // (except slices, which are handled in a separate arm above). - let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + + let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + PatKind::Wild } else { let old = self.behind_reference.replace(true); @@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> { && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); - let msg = "function pointers and unsized pointers in patterns behave \ - unpredictably and should not be relied upon. \ - See https://github.com/rust-lang/rust/issues/70861 for details."; - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::POINTER_STRUCTURAL_MATCH, id, span, - msg, - |lint| lint, + PointerPattern ); } PatKind::Constant { value: cv } } _ => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty()); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = InvalidPattern { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } }; @@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> { && mir_structural_match_violation // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty()).is_some() - { - self.saw_const_match_lint.set(true); + // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we // could get `Option`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace( - "in a pattern,", - "in a pattern, the constant's initializer must be trivial or", - ); - tcx.struct_span_lint_hir( + && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) + { + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH, id, span, - msg, - |lint| lint, + NontrivialStructuralMatch {non_sm_ty} ); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index d60e8722c..aba5429da 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -45,7 +45,7 @@ use std::cell::Cell; use std::cmp::{self, max, min, Ordering}; use std::fmt; -use std::iter::{once, IntoIterator}; +use std::iter::once; use std::ops::RangeInclusive; use smallvec::{smallvec, SmallVec}; @@ -67,6 +67,7 @@ use self::SliceKind::*; use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use crate::errors::{Overlap, OverlappingRangeEndpoints}; /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { @@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. #[derive(Clone, PartialEq, Eq)] -pub(super) struct IntRange { +pub(crate) struct IntRange { range: RangeInclusive, /// Keeps the bias used for encoding the range. It depends on the type of the range and /// possibly the pointer size of the current architecture. The algorithm ensures we never @@ -140,27 +141,22 @@ impl IntRange { ) -> Option { let ty = value.ty(); if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) { - let val = (|| { - match value { - mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just fall through, which - // is more general but much slower.) - return scalar.to_bits_or_ptr_internal(target_size).unwrap().left(); - } - mir::ConstantKind::Ty(c) => match c.kind() { - ty::ConstKind::Value(_) => bug!( - "encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val" - ), - _ => {} - }, - _ => {} + let val = if let mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) = value { + // For this specific pattern we can skip a lot of effort and go + // straight to the result, after doing a bit of checking. (We + // could remove this branch and just fall through, which + // is more general but much slower.) + scalar.to_bits_or_ptr_internal(target_size).unwrap().left()? + } else { + if let mir::ConstantKind::Ty(c) = value + && let ty::ConstKind::Value(_) = c.kind() + { + bug!("encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val"); } // This is a more general form of the previous case. - value.try_eval_bits(tcx, param_env, ty) - })()?; + value.try_eval_bits(tcx, param_env, ty)? + }; let val = val ^ bias; Some(IntRange { range: val..=val, bias }) } else { @@ -284,32 +280,21 @@ impl IntRange { return; } - let overlaps: Vec<_> = pats + let overlap: Vec<_> = pats .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| (self.intersection(&range).unwrap(), span)) + .map(|(range, span)| Overlap { + range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty), + span, + }) .collect(); - if !overlaps.is_empty() { - pcx.cx.tcx.struct_span_lint_hir( + if !overlap.is_empty() { + pcx.cx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, pcx.span, - "multiple patterns overlap on their endpoints", - |lint| { - for (int_range, span) in overlaps { - lint.span_label( - span, - &format!( - "this range overlaps on `{}`...", - int_range.to_pat(pcx.cx.tcx, pcx.ty) - ), - ); - } - lint.span_label(pcx.span, "... with this range"); - lint.note("you likely meant to write mutually exclusive ranges"); - lint - }, + OverlappingRangeEndpoints { overlap, range: pcx.span }, ); } } @@ -404,7 +389,7 @@ impl SplitIntRange { } /// Iterate over the contained ranges. - fn iter<'a>(&'a self) -> impl Iterator + Captures<'a> { + fn iter(&self) -> impl Iterator + Captures<'_> { use IntBorder::*; let self_range = Self::to_borders(self.range.clone()); @@ -612,7 +597,7 @@ impl SplitVarLenSlice { } /// Iterate over the partition of this slice. - fn iter<'a>(&'a self) -> impl Iterator + Captures<'a> { + fn iter(&self) -> impl Iterator + Captures<'_> { let smaller_lengths = match self.array_len { // The only admissible fixed-length slice is one of the array size. Whether `max_slice` // is fixed-length or variable-length, it will be the only relevant slice to output diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 48a231a6c..3a6ef87c9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -2,14 +2,16 @@ mod check_match; mod const_to_pat; -mod deconstruct_pat; +pub(crate) mod deconstruct_pat; mod usefulness; pub(crate) use self::check_match::check_match; +pub(crate) use self::usefulness::MatchCheckCtxt; +use crate::errors::*; use crate::thir::util::UserAnnotatedTyHelpers; -use rustc_errors::struct_span_err; +use rustc_errors::error_code; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; @@ -127,10 +129,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hi: mir::ConstantKind<'tcx>, end: RangeEnd, span: Span, + lo_expr: Option<&hir::Expr<'tcx>>, + hi_expr: Option<&hir::Expr<'tcx>>, ) -> PatKind<'tcx> { assert_eq!(lo.ty(), ty); assert_eq!(hi.ty(), ty); let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env); + let max = || { + self.tcx + .layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty)) + .ok() + .unwrap() + .size + .unsigned_int_max() + }; match (end, cmp) { // `x..y` where `x < y`. // Non-empty because the range includes at least `x`. @@ -139,13 +151,27 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..y` where `x >= y`. The range is empty => error. (RangeEnd::Excluded, _) => { - struct_span_err!( - self.tcx.sess, - span, - E0579, - "lower range bound must be less than upper" - ) - .emit(); + let mut lower_overflow = false; + let mut higher_overflow = false; + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if lo.eval_bits(self.tcx, self.param_env, ty) != val { + lower_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if hi.eval_bits(self.tcx, self.param_env, ty) != val { + higher_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if !lower_overflow && !higher_overflow { + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span }); + } PatKind::Wild } // `x..=y` where `x == y`. @@ -156,23 +182,34 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..=y` where `x > y` hence the range is empty => error. (RangeEnd::Included, _) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0030, - "lower range bound must be less than or equal to upper" - ); - err.span_label(span, "lower bound larger than upper bound"); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "When matching against a range, the compiler \ - verifies that the range is non-empty. Range \ - patterns include both end-points, so this is \ - equivalent to requiring the start of the range \ - to be less than or equal to the end of the range.", - ); + let mut lower_overflow = false; + let mut higher_overflow = false; + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if lo.eval_bits(self.tcx, self.param_env, ty) != val { + lower_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if hi.eval_bits(self.tcx, self.param_env, ty) != val { + higher_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if !lower_overflow && !higher_overflow { + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { + span, + teach: if self.tcx.sess.teach(&error_code!(E0030)) { + Some(()) + } else { + None + }, + }); } - err.emit(); PatKind::Wild } } @@ -218,7 +255,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let (lp, hp) = (lo.as_ref().map(|(x, _)| x), hi.as_ref().map(|(x, _)| x)); let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) { - Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span), + Some((lc, hc)) => { + self.lower_pattern_range(ty, lc, hc, end, lo_span, lo_expr, hi_expr) + } None => { let msg = &format!( "found bad range pattern `{:?}` outside of error recovery", @@ -501,7 +540,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + self.tcx.sess.emit_err(CouldNotEvalConstPattern { span }); return pat_from_kind(PatKind::Wild); } }; @@ -548,11 +587,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { Err(ErrorHandled::TooGeneric) => { // While `Reported | Linted` cases will have diagnostics emitted already // it is not true for TooGeneric case, so we need to give user more information. - self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); + self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span }); pat_from_kind(PatKind::Wild) } Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + self.tcx.sess.emit_err(CouldNotEvalConstPattern { span }); pat_from_kind(PatKind::Wild) } } @@ -584,7 +623,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind, mir::ConstantKind::Unevaluated(..) => { // If we land here it means the const can't be evaluated because it's `TooGeneric`. - self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); + self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span }); return PatKind::Wild; } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 3e370a053..be66d0d47 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -291,9 +291,8 @@ use self::ArmType::*; use self::Usefulness::*; - -use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label}; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; +use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; use rustc_data_structures::captures::Captures; @@ -743,31 +742,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { } } -/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` -/// is not exhaustive enough. -/// -/// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. -fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - scrut_ty: Ty<'tcx>, - sp: Span, - hir_id: HirId, - witnesses: Vec>, -) { - cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| { - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); - lint.help( - "ensure that all variants are matched explicitly by adding the suggested match arms", - ); - lint.note(&format!( - "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - scrut_ty, - )); - lint - }); -} - /// Algorithm from . /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -845,7 +819,7 @@ fn is_useful<'p, 'tcx>( // Opaque types can't get destructured/split, but the patterns can // actually hint at hidden types, so we use the patterns' types instead. - if let ty::Opaque(..) = ty.kind() { + if let ty::Alias(ty::Opaque, ..) = ty.kind() { if let Some(row) = rows.first() { ty = row.head().ty(); } @@ -913,7 +887,19 @@ fn is_useful<'p, 'tcx>( .collect::>() }; - lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns); + // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` + // is not exhaustive enough. + // + // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. + cx.tcx.emit_spanned_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + hir_id, + pcx.span, + NonExhaustiveOmittedPattern { + scrut_ty: pcx.ty, + uncovered: Uncovered::new(pcx.span, pcx.cx, patterns), + }, + ); } ret.extend(usefulness); -- cgit v1.2.3