diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_mir_build/src/thir/constant.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/cx/block.rs | 18 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/cx/expr.rs | 79 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/cx/mod.rs | 114 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 149 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs | 101 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs | 67 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/mod.rs | 135 | ||||
-rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 47 |
9 files changed, 455 insertions, 257 deletions
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index a7e4403a2..f626571b5 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -44,7 +44,7 @@ pub(crate) fn lit_to_const<'tcx>( } (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), - (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported), + (ast::LitKind::Err, _) => return Err(LitToConstError::Reported), _ => return Err(LitToConstError::TypeError), }; diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index dccaa61ed..321353ca2 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -9,13 +9,13 @@ use rustc_index::vec::Idx; use rustc_middle::ty::CanonicalUserTypeAnnotation; impl<'tcx> Cx<'tcx> { - pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> Block { + pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> BlockId { // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. let stmts = self.mirror_stmts(block.hir_id.local_id, block.stmts); let opt_destruction_scope = self.region_scope_tree.opt_destruction_scope(block.hir_id.local_id); - Block { + let block = Block { targeted_by_break: block.targeted_by_break, region_scope: region::Scope { id: block.hir_id.local_id, @@ -34,7 +34,9 @@ impl<'tcx> Cx<'tcx> { BlockSafety::ExplicitUnsafe(block.hir_id) } }, - } + }; + + self.thir.blocks.push(block) } fn mirror_stmts( @@ -85,21 +87,21 @@ impl<'tcx> Cx<'tcx> { { debug!("mirror_stmts: user_ty={:?}", user_ty); let annotation = CanonicalUserTypeAnnotation { - user_ty, + user_ty: Box::new(user_ty), span: ty.span, inferred_ty: self.typeck_results.node_type(ty.hir_id), }; - pattern = Pat { + pattern = Box::new(Pat { ty: pattern.ty, span: pattern.span, - kind: Box::new(PatKind::AscribeUserType { + kind: PatKind::AscribeUserType { ascription: Ascription { annotation, variance: ty::Variance::Covariant, }, subpattern: pattern, - }), - }; + }, + }); } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 985601712..c7a7c3e3f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> { _ => None, }; + trace!(?expr.ty); + // Now apply adjustments, if any. for adjustment in self.typeck_results.expr_adjustments(hir_expr) { trace!(?expr, ?adjustment); @@ -56,6 +58,8 @@ impl<'tcx> Cx<'tcx> { self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span)); } + trace!(?expr.ty, "after adjustments"); + // Next, wrap this up in the expr's scope. expr = Expr { temp_lifetime, @@ -108,8 +112,8 @@ impl<'tcx> Cx<'tcx> { // // ^ error message points at this expression. // } let mut adjust_span = |expr: &mut Expr<'tcx>| { - if let ExprKind::Block { body } = &expr.kind { - if let Some(last_expr) = body.expr { + if let ExprKind::Block { block } = expr.kind { + if let Some(last_expr) = self.thir[block].expr { span = self.thir[last_expr].span; expr.span = span; } @@ -155,6 +159,7 @@ impl<'tcx> Cx<'tcx> { Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { ExprKind::AddressOf { mutability, arg: self.thir.exprs.push(expr) } } + Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) }, }; Expr { temp_lifetime, ty: adjustment.target, span, kind } @@ -261,15 +266,19 @@ impl<'tcx> Cx<'tcx> { let kind = match expr.kind { // Here comes the interesting stuff: - hir::ExprKind::MethodCall(segment, ref args, fn_span) => { + hir::ExprKind::MethodCall(segment, receiver, ref args, fn_span) => { // Rewrite a.b(c) into UFCS form like Trait::b(a, c) let expr = self.method_callee(expr, segment.ident.span, None); // When we apply adjustments to the receiver, use the span of // the overall method call for better diagnostics. args[0] // is guaranteed to exist, since a method call always has a receiver. - let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span)); - tracing::info!("Using method span: {:?}", expr.span); - let args = self.mirror_exprs(args); + let old_adjustment_span = + self.adjustment_span.replace((receiver.hir_id, expr_span)); + info!("Using method span: {:?}", expr.span); + let args = std::iter::once(receiver) + .chain(args.iter()) + .map(|expr| self.mirror_expr(expr)) + .collect(); self.adjustment_span = old_adjustment_span; ExprKind::Call { ty: expr.ty, @@ -329,7 +338,7 @@ impl<'tcx> Cx<'tcx> { if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value { *did = adt_def.did(); } - u_ty + Box::new(u_ty) }); debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty); @@ -341,7 +350,7 @@ impl<'tcx> Cx<'tcx> { expr: self.mirror_expr(e), }) .collect(); - ExprKind::Adt(Box::new(Adt { + ExprKind::Adt(Box::new(AdtExpr { adt_def, substs, variant_index: index, @@ -369,7 +378,7 @@ impl<'tcx> Cx<'tcx> { ExprKind::AddressOf { mutability, arg: self.mirror_expr(arg) } } - hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: self.mirror_block(blk) }, + hir::ExprKind::Block(ref blk, _) => ExprKind::Block { block: self.mirror_block(blk) }, hir::ExprKind::Assign(ref lhs, ref rhs, _) => { ExprKind::Assign { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs) } @@ -464,9 +473,9 @@ impl<'tcx> Cx<'tcx> { ty::Adt(adt, substs) => match adt.adt_kind() { AdtKind::Struct | AdtKind::Union => { let user_provided_types = self.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(expr.hir_id).copied(); + let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); - ExprKind::Adt(Box::new(Adt { + ExprKind::Adt(Box::new(AdtExpr { adt_def: *adt, variant_index: VariantIdx::new(0), substs, @@ -490,9 +499,10 @@ impl<'tcx> Cx<'tcx> { let index = adt.variant_index_with_id(variant_id); let user_provided_types = self.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(expr.hir_id).copied(); + let user_ty = + user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); - ExprKind::Adt(Box::new(Adt { + ExprKind::Adt(Box::new(AdtExpr { adt_def: *adt, variant_index: index, substs, @@ -547,7 +557,13 @@ impl<'tcx> Cx<'tcx> { None => Vec::new(), }; - ExprKind::Closure { closure_id: def_id, substs, upvars, movability, fake_reads } + ExprKind::Closure(Box::new(ClosureExpr { + closure_id: def_id, + substs, + upvars, + movability, + fake_reads, + })) } hir::ExprKind::Path(ref qpath) => { @@ -555,7 +571,7 @@ impl<'tcx> Cx<'tcx> { self.convert_path_expr(expr, res) } - hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm { + hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm(Box::new(InlineAsmExpr { template: asm.template, operands: asm .operands @@ -614,7 +630,7 @@ impl<'tcx> Cx<'tcx> { .collect(), options: asm.options, line_spans: asm.line_spans, - }, + })), hir::ExprKind::ConstBlock(ref anon_const) => { let ty = self.typeck_results().node_type(anon_const.hir_id); @@ -679,8 +695,8 @@ impl<'tcx> Cx<'tcx> { let body = self.thir.exprs.push(Expr { ty: block_ty, temp_lifetime, - span: block.span, - kind: ExprKind::Block { body: block }, + span: self.thir[block].span, + kind: ExprKind::Block { block }, }); ExprKind::Loop { body } } @@ -712,14 +728,17 @@ impl<'tcx> Cx<'tcx> { }); debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty); - ExprKind::ValueTypeAscription { source: cast_expr, user_ty: Some(*user_ty) } + ExprKind::ValueTypeAscription { + source: cast_expr, + user_ty: Some(Box::new(*user_ty)), + } } else { cast } } hir::ExprKind::Type(ref source, ref ty) => { let user_provided_types = self.typeck_results.user_provided_types(); - let user_ty = user_provided_types.get(ty.hir_id).copied(); + let user_ty = user_provided_types.get(ty.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); let mirrored = self.mirror_expr(source); if source.is_syntactic_place_expr() { @@ -748,7 +767,7 @@ impl<'tcx> Cx<'tcx> { &mut self, hir_id: hir::HirId, res: Res, - ) -> Option<ty::CanonicalUserType<'tcx>> { + ) -> Option<Box<ty::CanonicalUserType<'tcx>>> { debug!("user_substs_applied_to_res: res={:?}", res); let user_provided_type = match res { // A reference to something callable -- e.g., a fn, method, or @@ -759,7 +778,7 @@ impl<'tcx> Cx<'tcx> { | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { - self.typeck_results().user_provided_types().get(hir_id).copied() + self.typeck_results().user_provided_types().get(hir_id).copied().map(Box::new) } // A unit struct/variant which is used as a value (e.g., @@ -767,11 +786,11 @@ impl<'tcx> Cx<'tcx> { // this variant -- but with the substitutions given by the // user. Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => { - self.user_substs_applied_to_ty_of_hir_id(hir_id) + self.user_substs_applied_to_ty_of_hir_id(hir_id).map(Box::new) } // `Self` is used in expression as a tuple struct constructor or a unit struct constructor - Res::SelfCtor(_) => self.user_substs_applied_to_ty_of_hir_id(hir_id), + Res::SelfCtor(_) => self.user_substs_applied_to_ty_of_hir_id(hir_id).map(Box::new), _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id), }; @@ -846,22 +865,22 @@ impl<'tcx> Cx<'tcx> { Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { let user_ty = self.user_substs_applied_to_res(expr.hir_id, res); - ExprKind::NamedConst { def_id, substs, user_ty: user_ty } + ExprKind::NamedConst { def_id, substs, user_ty } } Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => { let user_provided_types = self.typeck_results.user_provided_types(); - let user_provided_type = user_provided_types.get(expr.hir_id).copied(); - debug!("convert_path_expr: user_provided_type={:?}", user_provided_type); + let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); + debug!("convert_path_expr: user_ty={:?}", user_ty); let ty = self.typeck_results().node_type(expr.hir_id); match ty.kind() { // A unit struct/variant which is used as a value. // We return a completely different ExprKind here to account for this special case. - ty::Adt(adt_def, substs) => ExprKind::Adt(Box::new(Adt { + ty::Adt(adt_def, substs) => ExprKind::Adt(Box::new(AdtExpr { adt_def: *adt_def, variant_index: adt_def.variant_index_with_ctor_id(def_id), substs, - user_ty: user_provided_type, + user_ty, fields: Box::new([]), base: None, })), @@ -980,7 +999,7 @@ impl<'tcx> Cx<'tcx> { .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); let var_ty = place.base_ty; - // The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path + // The result of capture analysis in `rustc_hir_analysis/check/upvar.rs`represents a captured path // as it's seen for use within the closure and not at the time of closure creation. // // That is we see expect to see it start from a captured upvar and not something that is local diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index f7351a4ca..1d95d6b53 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -8,7 +8,9 @@ use crate::thir::util::UserAnnotatedTyHelpers; use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; use rustc_hir::HirId; use rustc_hir::Node; use rustc_middle::middle::region; @@ -27,6 +29,26 @@ pub(crate) fn thir_body<'tcx>( return Err(reported); } let expr = cx.mirror_expr(&body.value); + + let owner_id = hir.local_def_id_to_hir_id(owner_def.did); + if let Some(ref fn_decl) = hir.fn_decl_by_hir_id(owner_id) { + let closure_env_param = cx.closure_env_param(owner_def.did, owner_id); + let explicit_params = cx.explicit_params(owner_id, fn_decl, body); + cx.thir.params = closure_env_param.into_iter().chain(explicit_params).collect(); + + // The resume argument may be missing, in that case we need to provide it here. + // It will always be `()` in this case. + if tcx.def_kind(owner_def.did) == DefKind::Generator && body.params.is_empty() { + cx.thir.params.push(Param { + ty: tcx.mk_unit(), + pat: None, + ty_span: None, + self_kind: None, + hir_id: None, + }); + } + } + Ok((tcx.alloc_steal_thir(cx.thir), expr)) } @@ -44,11 +66,11 @@ struct Cx<'tcx> { tcx: TyCtxt<'tcx>, thir: Thir<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, + param_env: ty::ParamEnv<'tcx>, - pub(crate) region_scope_tree: &'tcx region::ScopeTree, - pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>, - pub(crate) rvalue_scopes: &'tcx RvalueScopes, + region_scope_tree: &'tcx region::ScopeTree, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + rvalue_scopes: &'tcx RvalueScopes, /// When applying adjustments to the expression /// with the given `HirId`, use the given `Span`, @@ -77,14 +99,94 @@ impl<'tcx> Cx<'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { + #[instrument(level = "debug", skip(self))] + fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Box<Pat<'tcx>> { let p = match self.tcx.hir().get(p.hir_id) { Node::Pat(p) => p, node => bug!("pattern became {:?}", node), }; pat_from_hir(self.tcx, self.param_env, self.typeck_results(), p) } + + fn closure_env_param(&self, owner_def: LocalDefId, owner_id: HirId) -> Option<Param<'tcx>> { + match self.tcx.def_kind(owner_def) { + DefKind::Closure => { + let closure_ty = self.typeck_results.node_type(owner_id); + + let ty::Closure(closure_def_id, closure_substs) = *closure_ty.kind() else { + bug!("closure expr does not have closure type: {:?}", closure_ty); + }; + + let bound_vars = self.tcx.mk_bound_variable_kinds(std::iter::once( + ty::BoundVariableKind::Region(ty::BrEnv), + )); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BrEnv, + }; + let env_region = ty::ReLateBound(ty::INNERMOST, br); + let closure_env_ty = + self.tcx.closure_env_ty(closure_def_id, closure_substs, env_region).unwrap(); + let liberated_closure_env_ty = self.tcx.erase_late_bound_regions( + ty::Binder::bind_with_vars(closure_env_ty, bound_vars), + ); + let env_param = Param { + ty: liberated_closure_env_ty, + pat: None, + ty_span: None, + self_kind: None, + hir_id: None, + }; + + Some(env_param) + } + DefKind::Generator => { + let gen_ty = self.typeck_results.node_type(owner_id); + let gen_param = + Param { ty: gen_ty, pat: None, ty_span: None, self_kind: None, hir_id: None }; + Some(gen_param) + } + _ => None, + } + } + + fn explicit_params<'a>( + &'a mut self, + owner_id: HirId, + fn_decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + ) -> impl Iterator<Item = Param<'tcx>> + 'a { + let fn_sig = self.typeck_results.liberated_fn_sigs()[owner_id]; + + body.params.iter().enumerate().map(move |(index, param)| { + let ty_span = fn_decl + .inputs + .get(index) + // Make sure that inferred closure args have no type span + .and_then(|ty| if param.pat.span != ty.span { Some(ty.span) } else { None }); + + let self_kind = if index == 0 && fn_decl.implicit_self.has_implicit_self() { + Some(fn_decl.implicit_self) + } else { + None + }; + + // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` + // (as it's created inside the body itself, not passed in from outside). + let ty = if fn_decl.c_variadic && index == fn_decl.inputs.len() { + let va_list_did = self.tcx.require_lang_item(LangItem::VaList, Some(param.span)); + + self.tcx + .bound_type_of(va_list_did) + .subst(self.tcx, &[self.tcx.lifetimes.re_erased.into()]) + } else { + fn_sig.inputs()[index] + }; + + let pat = self.pattern_from_hir(param.pat); + Param { pat: Some(pat), ty, ty_span, self_kind, hir_id: Some(param.hir_id) } + }) + } } impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'tcx> { 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 063c07647..858129c74 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -7,7 +7,7 @@ use super::{PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, + error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; @@ -347,27 +347,35 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let span_end = affix.last().unwrap().unwrap().0; let span = span_start.to(span_end); let cnt = affix.len(); - cx.tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, top, span, |lint| { - let s = pluralize!(cnt); - let mut diag = lint.build(&format!("{kind} irrefutable pattern{s} in let chain")); - diag.note(&format!( - "{these} pattern{s} will always match", - these = pluralize!("this", cnt), - )); - diag.help(&format!( - "consider moving {} {suggestion}", - if cnt > 1 { "them" } else { "it" } - )); - diag.emit() - }); + 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. // Check if the let source is while, for there is no alternative place to put a prefix, // and we shouldn't lint. + // For let guards inside a match, prefixes might use bindings of the match pattern, + // so can't always be moved out. + // FIXME: Add checking whether the bindings are actually used in the prefix, + // and lint if they are not. let let_source = let_source_parent(self.tcx, top, None); - if !matches!(let_source, LetSource::WhileLet) { + if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) { // Emit the lint let prefix = &chain_refutabilities[..until]; lint_affix(prefix, "leading", "outside of the construct"); @@ -487,12 +495,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { ], Applicability::HasPlaceholders, ); - if !bindings.is_empty() && cx.tcx.sess.is_nightly_build() { + if !bindings.is_empty() { err.span_suggestion_verbose( semi_span.shrink_to_lo(), &format!( - "alternatively, on nightly, you might want to use \ - `#![feature(let_else)]` to handle the variant{} that {} matched", + "alternatively, you might want to use \ + let else to handle the variant{} that {} matched", pluralize!(witnesses.len()), match witnesses.len() { 1 => "isn't", @@ -561,26 +569,28 @@ fn check_for_bindings_named_same_as_variants( 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()); - let mut err = lint.build(&format!( - "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", - ident, ty_path - )); - err.code(error_code!(E0170)); + lint.code(error_code!(E0170)); + // 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 { - err.span_suggestion( + lint.span_suggestion( p.span, "to match on the variant, qualify the path", format!("{}::{}", ty_path, ident), Applicability::MachineApplicable, ); } - err.emit(); + + lint }, ) } @@ -598,14 +608,13 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { } fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) { - tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| { - let mut err = lint.build("unreachable pattern"); + 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. - err.span_label(span, "unreachable pattern"); - err.span_label(catchall, "matches any value"); + lint.span_label(span, "unreachable pattern"); + lint.span_label(catchall, "matches any value"); } - err.emit(); + lint }); } @@ -621,6 +630,11 @@ fn irrefutable_let_patterns( count: usize, span: Span, ) { + let span = match source { + LetSource::LetElse(span) => span, + _ => span, + }; + macro_rules! emit_diag { ( $lint:expr, @@ -630,18 +644,23 @@ fn irrefutable_let_patterns( ) => {{ let s = pluralize!(count); let these = pluralize!("this", count); - let mut diag = $lint.build(&format!("irrefutable {} pattern{s}", $source_name)); - diag.note(&format!("{these} pattern{s} will always match, so the {}", $note_sufix)); - diag.help(concat!("consider ", $help_sufix)); - diag.emit() + 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)) + }, + ) }}; } - let span = match source { - LetSource::LetElse(span) => span, - _ => span, - }; - tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source { + match source { LetSource::GenericLet => { emit_diag!(lint, "`let`", "`let` is useless", "removing `let`"); } @@ -677,7 +696,7 @@ fn irrefutable_let_patterns( "instead using a `loop { ... }` with a `let` inside it" ); } - }); + }; } fn is_let_irrefutable<'p, 'tcx>( @@ -849,22 +868,22 @@ fn non_exhaustive_match<'p, 'tcx>( )); } [.., prev, last] if prev.span.eq_ctxt(last.span) => { - if let Ok(snippet) = sm.span_to_snippet(prev.span.between(last.span)) { - let comma = if matches!(last.body.kind, hir::ExprKind::Block(..)) - && last.span.eq_ctxt(last.body.span) - { - "" - } else { - "," - }; + let comma = if matches!(last.body.kind, hir::ExprKind::Block(..)) + && last.span.eq_ctxt(last.body.span) + { + "" + } else { + "," + }; + let spacing = if sm.is_multiline(prev.span.between(last.span)) { + sm.indentation_before(last.span).map(|indent| format!("\n{indent}")) + } else { + Some(" ".to_string()) + }; + if let Some(spacing) = spacing { suggestion = Some(( last.span.shrink_to_hi(), - format!( - "{}{}{} => todo!()", - comma, - snippet.strip_prefix(',').unwrap_or(&snippet), - pattern - ), + format!("{}{}{} => todo!()", comma, spacing, pattern), )); } } @@ -985,8 +1004,8 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( } /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`. -fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool { - !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env) +fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId) -> bool { + !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx, cx.param_env) } /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns. @@ -1012,7 +1031,7 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa // Get the binding move, extract the mutability if by-ref. let mut_outer = match typeck_results.extract_binding_mode(sess, pat.hir_id, pat.span) { - Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id, pat.span) => { + Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id) => { // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); sub.each_binding(|_, hir_id, span, _| { @@ -1051,7 +1070,7 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`. _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction. }, - Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id, span) => { + Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id) => { conflicts_move.push((span, name)) // `ref mut?` + by-move conflict. } Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine. @@ -1136,10 +1155,14 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); - if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) = - parent_parent_node - { - return LetSource::LetElse(*span); + match parent_parent_node { + hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) => { + return LetSource::LetElse(*span); + } + hir::Node::Arm(hir::Arm { guard: Some(hir::Guard::If(_)), .. }) => { + return LetSource::IfLetGuard; + } + _ => {} } let parent_parent_parent = hir.get_parent_node(parent_parent); 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 d6dd0f017..ad12e0116 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,3 +1,4 @@ +use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -19,25 +20,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(super) fn const_to_pat( &self, cv: mir::ConstantKind<'tcx>, id: hir::HirId, span: Span, mir_structural_match_violation: bool, - ) -> Pat<'tcx> { - let pat = self.tcx.infer_ctxt().enter(|infcx| { - let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(cv, mir_structural_match_violation) - }); - - debug!(?pat); - pat + ) -> Box<Pat<'tcx>> { + let infcx = self.tcx.infer_ctxt().build(); + let mut convert = ConstToPat::new(self, id, span, infcx); + convert.to_pat(cv, mir_structural_match_violation) } } -struct ConstToPat<'a, 'tcx> { +struct ConstToPat<'tcx> { id: hir::HirId, span: Span, param_env: ty::ParamEnv<'tcx>, @@ -57,7 +54,7 @@ struct ConstToPat<'a, 'tcx> { behind_reference: Cell<bool>, // inference context used for checking `T: Structural` bounds. - infcx: InferCtxt<'a, 'tcx>, + infcx: InferCtxt<'tcx>, include_lint_checks: bool, @@ -73,21 +70,19 @@ mod fallback_to_const_ref { /// hoops to get a reference to the value. pub(super) struct FallbackToConstRef(()); - pub(super) fn fallback_to_const_ref<'a, 'tcx>( - c2p: &super::ConstToPat<'a, 'tcx>, - ) -> FallbackToConstRef { + pub(super) fn fallback_to_const_ref<'tcx>(c2p: &super::ConstToPat<'tcx>) -> FallbackToConstRef { assert!(c2p.behind_reference.get()); FallbackToConstRef(()) } } use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef}; -impl<'a, 'tcx> ConstToPat<'a, 'tcx> { +impl<'tcx> ConstToPat<'tcx> { fn new( pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, - infcx: InferCtxt<'a, 'tcx>, + infcx: InferCtxt<'tcx>, ) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { @@ -159,7 +154,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { &mut self, cv: mir::ConstantKind<'tcx>, mir_structural_match_violation: bool, - ) -> Pat<'tcx> { + ) -> Box<Pat<'tcx>> { trace!(self.treat_byte_string_as_slice); // This method is just a wrapper handling a validity check; the heavy lifting is // performed by the recursive `recur` method, which is not meant to be @@ -168,7 +163,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // once indirect_structural_match is a full fledged error, this // level of indirection can be eliminated - let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap(); + let inlined_const_as_pat = + self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| { + Box::new(Pat { + span: self.span, + ty: cv.ty(), + kind: PatKind::Constant { value: cv }, + }) + }); if self.include_lint_checks && !self.saw_const_match_error.get() { // If we were able to successfully convert the const to some pat, @@ -201,9 +203,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - |lint| { - lint.build(&msg).emit(); - }, + msg, + |lint| lint, ); } else { debug!( @@ -269,7 +270,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { &self, cv: mir::ConstantKind<'tcx>, mir_structural_match_violation: bool, - ) -> Result<Pat<'tcx>, FallbackToConstRef> { + ) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> { let id = self.id; let span = self.span; let tcx = self.tcx(); @@ -282,9 +283,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, - |lint| { - lint.build("floating-point types cannot be used in patterns").emit(); - }, + "floating-point types cannot be used in patterns", + |lint| lint, ); } PatKind::Constant { value: cv } @@ -336,15 +336,15 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, - |lint| { - let msg = format!( + DelayDm(|| { + format!( "to use a constant of type `{}` in a pattern, \ `{}` must be annotated with `#[derive(PartialEq, Eq)]`", cv.ty(), cv.ty(), - ); - lint.build(&msg).emit(); - }, + ) + }), + |lint| lint, ); } // Since we are behind a reference, we can just bubble the error up so we get a @@ -396,7 +396,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .map(|val| self.recur(*val, false)) .collect::<Result<_, _>>()?, slice: None, - suffix: Vec::new(), + suffix: Box::new([]), }, ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { // These are not allowed and will error elsewhere anyway. @@ -423,8 +423,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let old = self.behind_reference.replace(true); let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { - subpattern: Pat { - kind: Box::new(PatKind::Array { + subpattern: Box::new(Pat { + kind: PatKind::Array { prefix: tcx .destructure_mir_constant(param_env, array) .fields @@ -432,11 +432,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .map(|val| self.recur(*val, false)) .collect::<Result<_, _>>()?, slice: None, - suffix: vec![], - }), + suffix: Box::new([]), + }, span, ty: *pointee_ty, - }, + }), }; self.behind_reference.set(old); val @@ -449,8 +449,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let old = self.behind_reference.replace(true); let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { - subpattern: Pat { - kind: Box::new(PatKind::Slice { + subpattern: Box::new(Pat { + kind: PatKind::Slice { prefix: tcx .destructure_mir_constant(param_env, array) .fields @@ -458,11 +458,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .map(|val| self.recur(*val, false)) .collect::<Result<_, _>>()?, slice: None, - suffix: vec![], - }), + suffix: Box::new([]), + }, span, ty: tcx.mk_slice(elem_ty), - }, + }), }; self.behind_reference.set(old); val @@ -484,7 +484,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - |lint| {lint.build(&msg).emit();}, + msg, + |lint| lint, ); } PatKind::Constant { value: cv } @@ -505,7 +506,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // convert the dereferenced constant to a pattern that is the sub-pattern of the // deref pattern. _ => { - if !pointee_ty.is_sized(tcx.at(span), param_env) { + 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); @@ -533,7 +534,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => { PatKind::Constant { value: cv } } - ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => { + ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => { PatKind::Constant { value: cv } } // FIXME: these can have very surprising behaviour where optimization levels or other @@ -552,9 +553,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::POINTER_STRUCTURAL_MATCH, id, span, - |lint| { - lint.build(msg).emit(); - }, + msg, + |lint| lint, ); } PatKind::Constant { value: cv } @@ -590,12 +590,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH, id, span, - |lint| { - lint.build(&msg).emit(); - }, + msg, + |lint| lint, ); } - Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) }) + Ok(Box::new(Pat { span, ty: cv.ty(), kind })) } } 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 8d6f8efb6..595abc8f6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -71,9 +71,9 @@ use std::ops::RangeInclusive; /// 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>> { fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { - if let PatKind::Or { pats } = pat.kind.as_ref() { - for pat in pats { - expand(pat, vec); + if let PatKind::Or { pats } = &pat.kind { + for pat in pats.iter() { + expand(&pat, vec); } } else { vec.push(pat) @@ -252,10 +252,14 @@ impl IntRange { let kind = if lo == hi { PatKind::Constant { value: lo_const } } else { - PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) + PatKind::Range(Box::new(PatRange { + lo: lo_const, + hi: hi_const, + end: RangeEnd::Included, + })) }; - Pat { ty, span: DUMMY_SP, kind: Box::new(kind) } + Pat { ty, span: DUMMY_SP, kind } } /// Lint on likely incorrect range patterns (#63987) @@ -295,10 +299,10 @@ impl IntRange { lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, pcx.span, + "multiple patterns overlap on their endpoints", |lint| { - let mut err = lint.build("multiple patterns overlap on their endpoints"); for (int_range, span) in overlaps { - err.span_label( + lint.span_label( span, &format!( "this range overlaps on `{}`...", @@ -306,9 +310,9 @@ impl IntRange { ), ); } - err.span_label(pcx.span, "... with this range"); - err.note("you likely meant to write mutually exclusive ranges"); - err.emit(); + lint.span_label(pcx.span, "... with this range"); + lint.note("you likely meant to write mutually exclusive ranges"); + lint }, ); } @@ -984,10 +988,12 @@ impl<'tcx> SplitWildcard<'tcx> { .filter(|(_, v)| { // If `exhaustive_patterns` is enabled, we exclude variants known to be // uninhabited. - let is_uninhabited = is_exhaustive_pat_feature - && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module); - !is_uninhabited + !is_exhaustive_pat_feature + || v.inhabited_predicate(cx.tcx, *def).subst(cx.tcx, substs).apply( + cx.tcx, + cx.param_env, + cx.module, + ) }) .map(|(idx, _)| Variant(idx)) .collect(); @@ -1297,7 +1303,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); let ctor; let fields; - match pat.kind.as_ref() { + match &pat.kind { PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern), PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { @@ -1342,9 +1348,9 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { fields = Fields::singleton(cx, pat); } ty::Adt(adt, _) => { - ctor = match pat.kind.as_ref() { + ctor = match pat.kind { PatKind::Leaf { .. } => Single, - PatKind::Variant { variant_index, .. } => Variant(*variant_index), + PatKind::Variant { variant_index, .. } => Variant(variant_index), _ => bug!(), }; let variant = &adt.variant(ctor.variant_index_for_adt(*adt)); @@ -1402,7 +1408,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } } - &PatKind::Range(PatRange { lo, hi, end }) => { + &PatKind::Range(box PatRange { lo, hi, end }) => { let ty = lo.ty(); ctor = if let Some(int_range) = IntRange::from_range( cx.tcx, @@ -1429,7 +1435,8 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { FixedLen(prefix.len() + suffix.len()) }; ctor = Slice(Slice::new(array_len, kind)); - fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); + fields = + Fields::from_iter(cx, prefix.iter().chain(suffix.iter()).map(|p| mkpat(&*p))); } PatKind::Or { .. } => { ctor = Or; @@ -1442,15 +1449,15 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> { let is_wildcard = |pat: &Pat<'_>| { - matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) + matches!(pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) }; - let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); - let pat = match &self.ctor { + let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx))); + let kind = match &self.ctor { Single | Variant(_) => match self.ty.kind() { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .map(|(i, pattern)| FieldPat { field: Field::new(i), pattern }) .collect(), }, ty::Adt(adt_def, _) if adt_def.is_box() => { @@ -1485,7 +1492,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { FixedLen(_) => PatKind::Slice { prefix: subpatterns.collect(), slice: None, - suffix: vec![], + suffix: Box::new([]), }, VarLen(prefix, _) => { let mut subpatterns = subpatterns.peekable(); @@ -1504,14 +1511,18 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { subpatterns.next(); } } - let suffix: Vec<_> = subpatterns.collect(); + let suffix: Box<[_]> = subpatterns.collect(); let wild = Pat::wildcard_from_ty(self.ty); - PatKind::Slice { prefix, slice: Some(wild), suffix } + PatKind::Slice { + prefix: prefix.into_boxed_slice(), + slice: Some(Box::new(wild)), + suffix, + } } } } &Str(value) => PatKind::Constant { value }, - &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + &FloatRange(lo, hi, end) => PatKind::Range(Box::new(PatRange { lo, hi, end })), IntRange(range) => return range.to_pat(cx.tcx, self.ty), Wildcard | NonExhaustive => PatKind::Wild, Missing { .. } => bug!( @@ -1523,7 +1534,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } }; - Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) } + Pat { ty: self.ty, span: DUMMY_SP, kind } } pub(super) fn is_or_pat(&self) -> bool { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a13748a2d..2526522a2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -29,27 +29,27 @@ use rustc_span::{Span, Symbol}; use std::cmp::Ordering; #[derive(Clone, Debug)] -pub(crate) enum PatternError { +enum PatternError { AssocConstInPattern(Span), ConstParamInPattern(Span), StaticInPattern(Span), NonConstPath(Span), } -pub(crate) struct PatCtxt<'a, 'tcx> { - pub(crate) tcx: TyCtxt<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, - pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, - pub(crate) errors: Vec<PatternError>, +struct PatCtxt<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + errors: Vec<PatternError>, include_lint_checks: bool, } -pub(crate) fn pat_from_hir<'a, 'tcx>( +pub(super) fn pat_from_hir<'a, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, -) -> Pat<'tcx> { +) -> Box<Pat<'tcx>> { let mut pcx = PatCtxt::new(tcx, param_env, typeck_results); let result = pcx.lower_pattern(pat); if !pcx.errors.is_empty() { @@ -61,7 +61,7 @@ pub(crate) fn pat_from_hir<'a, 'tcx>( } impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - pub(crate) fn new( + fn new( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -69,12 +69,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { PatCtxt { tcx, param_env, typeck_results, errors: vec![], include_lint_checks: false } } - pub(crate) fn include_lint_checks(&mut self) -> &mut Self { + fn include_lint_checks(&mut self) -> &mut Self { self.include_lint_checks = true; self } - pub(crate) fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { // When implicit dereferences have been inserted in this pattern, the unadjusted lowered // pattern has the type that results *after* dereferencing. For example, in this code: // @@ -86,7 +86,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // ``` // // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is - // determined in rustc_typeck::check::match). The adjustments would be + // determined in rustc_hir_analysis::check::match). The adjustments would be // // `vec![&&Option<i32>, &Option<i32>]`. // @@ -97,13 +97,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let unadjusted_pat = self.lower_pattern_unadjusted(pat); self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( unadjusted_pat, - |pat, ref_ty| { + |pat: Box<_>, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); - Pat { + Box::new(Pat { span: pat.span, ty: *ref_ty, - kind: Box::new(PatKind::Deref { subpattern: pat }), - } + kind: PatKind::Deref { subpattern: pat }, + }) }, ) } @@ -113,7 +113,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> (PatKind<'tcx>, Option<Ascription<'tcx>>) { match self.lower_lit(expr) { - PatKind::AscribeUserType { ascription, subpattern: Pat { kind: box kind, .. } } => { + PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => { (kind, Some(ascription)) } kind => (kind, None), @@ -134,7 +134,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { match (end, cmp) { // `x..y` where `x < y`. // Non-empty because the range includes at least `x`. - (RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), + (RangeEnd::Excluded, Some(Ordering::Less)) => { + PatKind::Range(Box::new(PatRange { lo, hi, end })) + } // `x..y` where `x >= y`. The range is empty => error. (RangeEnd::Excluded, _) => { struct_span_err!( @@ -149,7 +151,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // `x..=y` where `x == y`. (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo }, // `x..=y` where `x < y`. - (RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), + (RangeEnd::Included, Some(Ordering::Less)) => { + PatKind::Range(Box::new(PatRange { lo, hi, end })) + } // `x..=y` where `x > y` hence the range is empty => error. (RangeEnd::Included, _) => { let mut err = struct_span_err!( @@ -196,8 +200,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } - fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + #[instrument(skip(self), level = "debug")] + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { let mut ty = self.typeck_results.node_type(pat.hir_id); + let mut span = pat.span; let kind = match pat.kind { hir::PatKind::Wild => PatKind::Wild, @@ -228,7 +234,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // constants somewhere. Have them on the range pattern. for end in &[lo, hi] { if let Some((_, Some(ascription))) = end { - let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) }; + let subpattern = Box::new(Pat { span: pat.span, ty, kind }); kind = PatKind::AscribeUserType { ascription: ascription.clone(), subpattern }; } @@ -258,6 +264,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Binding(_, id, ident, ref sub) => { + if let Some(ident_span) = ident.span.find_ancestor_inside(span) { + span = span.with_hi(ident_span.hi()); + } + let bm = *self .typeck_results .pat_binding_modes() @@ -322,14 +332,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, }; - Pat { span: pat.span, ty, kind: Box::new(kind) } + Box::new(Pat { span, ty, kind }) } fn lower_tuple_subpats( &mut self, pats: &'tcx [hir::Pat<'tcx>], expected_len: usize, - gap_pos: Option<usize>, + gap_pos: hir::DotDotPos, ) -> Vec<FieldPat<'tcx>> { pats.iter() .enumerate_and_adjust(expected_len, gap_pos) @@ -340,11 +350,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .collect() } - fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> { + fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Box<Pat<'tcx>>]> { pats.iter().map(|p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option<Pat<'tcx>> { + fn lower_opt_pattern( + &mut self, + pat: &'tcx Option<&'tcx hir::Pat<'tcx>>, + ) -> Option<Box<Pat<'tcx>>> { pat.as_ref().map(|p| self.lower_pattern(p)) } @@ -420,7 +433,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | DefKind::AssocTy, _, ) - | Res::SelfTy { .. } + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { let pattern_error = match res { @@ -436,12 +450,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) { debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); let annotation = CanonicalUserTypeAnnotation { - user_ty, + user_ty: Box::new(user_ty), span, inferred_ty: self.typeck_results.node_type(hir_id), }; kind = PatKind::AscribeUserType { - subpattern: Pat { span, ty, kind: Box::new(kind) }, + subpattern: Box::new(Pat { span, ty, kind }), ascription: Ascription { annotation, variance: ty::Variance::Covariant }, }; } @@ -453,11 +467,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. #[instrument(skip(self), level = "debug")] - fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { + fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Box<Pat<'tcx>> { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); - let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) }; + let pat_from_kind = |kind| Box::new(Pat { span, ty, kind }); let (def_id, is_associated_const) = match res { Res::Def(DefKind::Const, def_id) => (def_id, false), @@ -469,7 +483,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Use `Reveal::All` here because patterns are always monomorphic even if their function // isn't. let param_env_reveal_all = self.param_env.with_reveal_all_normalized(self.tcx); - let substs = self.typeck_results.node_substs(id); + // N.B. There is no guarantee that substs collected in typeck results are fully normalized, + // so they need to be normalized in order to pass to `Instance::resolve`, which will ICE + // if given unnormalized types. + let substs = self + .tcx + .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_substs(id)); let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { Ok(Some(i)) => i, Ok(None) => { @@ -505,13 +524,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let user_provided_types = self.typeck_results().user_provided_types(); if let Some(&user_ty) = user_provided_types.get(id) { let annotation = CanonicalUserTypeAnnotation { - user_ty, + user_ty: Box::new(user_ty), span, inferred_ty: self.typeck_results().node_type(id), }; - Pat { + Box::new(Pat { span, - kind: Box::new(PatKind::AscribeUserType { + kind: PatKind::AscribeUserType { subpattern: pattern, ascription: Ascription { annotation, @@ -519,9 +538,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// `variance` field documentation for details. variance: ty::Variance::Contravariant, }, - }), + }, ty: const_.ty(), - } + }) } else { pattern } @@ -553,23 +572,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let value = value.eval(self.tcx, self.param_env); match value { - mir::ConstantKind::Ty(c) => { - match c.kind() { - ConstKind::Param(_) => { - self.errors.push(PatternError::ConstParamInPattern(span)); - return PatKind::Wild; - } - ConstKind::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"); - return PatKind::Wild; - } - _ => bug!("Expected either ConstKind::Param or ConstKind::Unevaluated"), + mir::ConstantKind::Ty(c) => match c.kind() { + ConstKind::Param(_) => { + self.errors.push(PatternError::ConstParamInPattern(span)); + return PatKind::Wild; } + _ => bug!("Expected ConstKind::Param"), + }, + 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"); + return PatKind::Wild; } - mir::ConstantKind::Val(_, _) => *self.const_to_pat(value, id, span, false).kind, } } @@ -580,7 +595,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { let (lit, neg) = match expr.kind { hir::ExprKind::Path(ref qpath) => { - return *self.lower_path(qpath, expr.hir_id, expr.span).kind; + return self.lower_path(qpath, expr.hir_id, expr.span).kind; } hir::ExprKind::ConstBlock(ref anon_const) => { return self.lower_inline_const(anon_const, expr.hir_id, expr.span); @@ -598,7 +613,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) { - Ok(constant) => *self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, + Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, Err(LitToConstError::Reported) => PatKind::Wild, Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } @@ -615,7 +630,7 @@ impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { } } -pub(crate) trait PatternFoldable<'tcx>: Sized { +trait PatternFoldable<'tcx>: Sized { fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { self.super_fold_with(folder) } @@ -623,7 +638,7 @@ pub(crate) trait PatternFoldable<'tcx>: Sized { fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self; } -pub(crate) trait PatternFolder<'tcx>: Sized { +trait PatternFolder<'tcx>: Sized { fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { pattern.super_fold_with(self) } @@ -646,6 +661,12 @@ impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec<T> { } } +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box<[T]> { + fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } +} + impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option<T> { fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { self.as_ref().map(|t| t.fold_with(folder)) @@ -665,7 +686,7 @@ macro_rules! ClonePatternFoldableImpls { } ClonePatternFoldableImpls! { <'tcx> - Span, Field, Mutability, Symbol, LocalVarId, usize, ty::Const<'tcx>, + Span, Field, Mutability, Symbol, LocalVarId, usize, Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>, SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, UserTypeProjection, CanonicalUserTypeAnnotation<'tcx> @@ -732,7 +753,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { PatKind::Deref { subpattern: subpattern.fold_with(folder) } } PatKind::Constant { value } => PatKind::Constant { value }, - PatKind::Range(range) => PatKind::Range(range), + PatKind::Range(ref range) => PatKind::Range(range.clone()), PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice { prefix: prefix.fold_with(folder), slice: slice.fold_with(folder), diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 0a660ef30..8dc9976ea 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -364,8 +364,8 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. #[derive(Clone)] -struct PatStack<'p, 'tcx> { - pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, +pub(crate) struct PatStack<'p, 'tcx> { + pub(crate) pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { @@ -403,6 +403,21 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { }) } + // Recursively expand all patterns into their subpatterns and push each `PatStack` to matrix. + fn expand_and_extend<'a>(&'a self, matrix: &mut Matrix<'p, 'tcx>) { + if !self.is_empty() && self.head().is_or_pat() { + for pat in self.head().iter_fields() { + let mut new_patstack = PatStack::from_pattern(pat); + new_patstack.pats.extend_from_slice(&self.pats[1..]); + if !new_patstack.is_empty() && new_patstack.head().is_or_pat() { + new_patstack.expand_and_extend(matrix); + } else if !new_patstack.is_empty() { + matrix.push(new_patstack); + } + } + } + } + /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations. /// /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing @@ -436,7 +451,7 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { /// A 2D matrix. #[derive(Clone)] pub(super) struct Matrix<'p, 'tcx> { - patterns: Vec<PatStack<'p, 'tcx>>, + pub patterns: Vec<PatStack<'p, 'tcx>>, } impl<'p, 'tcx> Matrix<'p, 'tcx> { @@ -453,7 +468,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// expands it. fn push(&mut self, row: PatStack<'p, 'tcx>) { if !row.is_empty() && row.head().is_or_pat() { - self.patterns.extend(row.expand_or_pat()); + row.expand_and_extend(self); } else { self.patterns.push(row); } @@ -731,7 +746,7 @@ 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_typeck/src/check/pat.rs`. +/// 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>, @@ -739,9 +754,8 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( hir_id: HirId, witnesses: Vec<DeconstructedPat<'p, 'tcx>>, ) { - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| { - let mut lint = build.build("some variants are not matched explicitly"); + 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", @@ -750,7 +764,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", scrut_ty, )); - lint.emit(); + lint }); } @@ -776,7 +790,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( /// `is_under_guard` is used to inform if the pattern has a guard. If it /// has one it must not be inserted into the matrix. This shouldn't be /// relied on for soundness. -#[instrument(level = "debug", skip(cx, matrix, hir_id))] +#[instrument(level = "debug", skip(cx, matrix, hir_id), ret)] fn is_useful<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, @@ -827,7 +841,15 @@ fn is_useful<'p, 'tcx>( } } } else { - let ty = v.head().ty(); + let mut ty = v.head().ty(); + + // 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 Some(row) = rows.first() { + ty = row.head().ty(); + } + } let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; @@ -863,7 +885,7 @@ fn is_useful<'p, 'tcx>( // that has the potential to trigger the `non_exhaustive_omitted_patterns` lint. // To understand the workings checkout `Constructor::split` and `SplitWildcard::new/into_ctors` if is_non_exhaustive_and_wild - // We check that the match has a wildcard pattern and that that wildcard is useful, + // We check that the match has a wildcard pattern and that wildcard is useful, // meaning there are variants that are covered by the wildcard. Without the check // for `witness_preference` the lint would trigger on `if let NonExhaustiveEnum::A = foo {}` && usefulness.is_useful() && matches!(witness_preference, RealArm) @@ -902,7 +924,6 @@ fn is_useful<'p, 'tcx>( v.head().set_reachable(); } - debug!(?ret); ret } |