From fa74a9e6aa525a285cfd530cdea2ddeb9fca013c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Jul 2024 16:13:55 +0200 Subject: [PATCH 1/5] valtree construction: keep track of which type was valtree-incompatible --- compiler/rustc_codegen_ssa/src/mir/constant.rs | 5 +++-- compiler/rustc_const_eval/src/const_eval/mod.rs | 10 +++++----- .../rustc_const_eval/src/const_eval/valtrees.rs | 16 ++++++++-------- compiler/rustc_infer/src/infer/mod.rs | 6 +++--- compiler/rustc_middle/src/mir/interpret/error.rs | 8 +++++--- compiler/rustc_middle/src/query/erase.rs | 7 ++++--- compiler/rustc_middle/src/ty/consts.rs | 3 +-- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 7 +++---- .../rustc_trait_selection/src/solve/delegate.rs | 4 ++-- .../src/traits/auto_trait.rs | 4 ++-- .../clippy/clippy_lints/src/non_copy_const.rs | 2 +- 11 files changed, 37 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 822f5c2c44a2e..35e9a3b7dc206 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -37,13 +37,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn eval_unevaluated_mir_constant_to_valtree( &self, constant: &mir::ConstOperand<'tcx>, - ) -> Result>, ErrorHandled> { + ) -> Result, Ty<'tcx>>, ErrorHandled> { let uv = match self.monomorphize(constant.const_) { mir::Const::Unevaluated(uv, _) => uv.shrink(), mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to old-style // simd_shuffle (passing as argument instead of as a generic param). - rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Some(valtree)), + rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate @@ -70,6 +70,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = self .eval_unevaluated_mir_constant_to_valtree(constant) .ok() + .map(|x| x.ok()) .flatten() .map(|val| { let field_ty = ty.builtin_index().unwrap(); diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 4ae4816e33ab9..3a6dc81eff11f 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -27,15 +27,15 @@ pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value}; // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. const VALTREE_MAX_NODES: usize = 100000; -pub(crate) enum ValTreeCreationError { +pub(crate) enum ValTreeCreationError<'tcx> { NodesOverflow, /// Values of this type, or this particular value, are not supported as valtrees. - NonSupportedType, + NonSupportedType(Ty<'tcx>), } -pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError>; +pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError<'tcx>>; -impl From> for ValTreeCreationError { - fn from(err: InterpErrorInfo<'_>) -> Self { +impl<'tcx> From> for ValTreeCreationError<'tcx> { + fn from(err: InterpErrorInfo<'tcx>) -> Self { ty::tls::with(|tcx| { bug!( "Unexpected Undefined Behavior error during valtree construction: {}", diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 2e8ad445cf5e8..3bc01510730b4 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -120,13 +120,13 @@ fn const_to_valtree_inner<'tcx>( // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. if matches!(val.layout.abi, Abi::ScalarPair(..)) { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); } let val = val.to_scalar(); // We are in the CTFE machine, so ptr-to-int casts will fail. // This can only be `Ok` if `val` already is an integer. let Ok(val) = val.try_to_scalar_int() else { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); }; // It's just a ScalarInt! Ok(ty::ValTree::Leaf(val)) @@ -134,7 +134,7 @@ fn const_to_valtree_inner<'tcx>( // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType), + ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Ref(_, _, _) => { let derefd_place = ecx.deref_pointer(place)?; @@ -148,7 +148,7 @@ fn const_to_valtree_inner<'tcx>( // resolving their backing type, even if we can do that at const eval time. We may // hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future, // but it is unclear if this is useful. - ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), + ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Tuple(elem_tys) => { branches(ecx, place, elem_tys.len(), None, num_nodes) @@ -156,7 +156,7 @@ fn const_to_valtree_inner<'tcx>( ty::Adt(def, _) => { if def.is_union() { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") } @@ -180,7 +180,7 @@ fn const_to_valtree_inner<'tcx>( | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) - | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType), + | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType(ty)), } } @@ -251,7 +251,7 @@ pub(crate) fn eval_to_valtree<'tcx>( let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); match valtree_result { - Ok(valtree) => Ok(Some(valtree)), + Ok(valtree) => Ok(Ok(valtree)), Err(err) => { let did = cid.instance.def_id(); let global_const_id = cid.display(tcx); @@ -262,7 +262,7 @@ pub(crate) fn eval_to_valtree<'tcx>( tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id }); Err(handled.into()) } - ValTreeCreationError::NonSupportedType => Ok(None), + ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)), } } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index cfef1f1301564..c9073d8c23e5a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1427,17 +1427,17 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, ) -> Result, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Some(val)) => Ok(ty::Const::new_value( + Ok(Ok(val)) => Ok(ty::Const::new_value( self.tcx, val, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), )), - Ok(None) => { + Ok(Err(bad_ty)) => { let tcx = self.tcx; let def_id = unevaluated.def; span_bug!( tcx.def_span(def_id), - "unable to construct a constant value for the unevaluated constant {:?}", + "unable to construct a valtree for the unevaluated constant {:?}: type {bad_ty} is not valtree-compatible", unevaluated ); } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 6a8498abaf933..9df19565ab383 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -90,9 +90,11 @@ TrivialTypeTraversalImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; -/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed. -/// This is needed in `thir::pattern::lower_inline_const`. -pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; +/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed +/// because the value containts something of type `ty` that is not valtree-compatible. +/// The caller can then show an appropriate error; the query does not have the +/// necssary context to give good user-facing errors for this case. +pub type EvalToValTreeResult<'tcx> = Result, Ty<'tcx>>, ErrorHandled>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 301c9911b44cd..d9fa5b02f7f74 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -157,9 +157,10 @@ impl EraseType for Result, mir::interpret::ErrorHandled> { type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()]; } -impl EraseType for Result>, mir::interpret::ErrorHandled> { - type Result = - [u8; size_of::>, mir::interpret::ErrorHandled>>()]; +impl EraseType for Result, Ty<'_>>, mir::interpret::ErrorHandled> { + type Result = [u8; size_of::< + Result, Ty<'static>>, mir::interpret::ErrorHandled>, + >()]; } impl EraseType for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 32d01d07c17e7..d4c39a25a1f28 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -328,8 +328,7 @@ impl<'tcx> Const<'tcx> { let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let Some(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? - else { + let Ok(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? else { // This can happen when we run on ill-typed code. let e = tcx.dcx().span_delayed_bug( span, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index fd778ef78a3ee..25b7d24de3979 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -581,8 +581,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .tcx .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) .map(|val| match val { - Some(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), - None => mir::Const::Val( + Ok(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), + Err(_) => mir::Const::Val( self.tcx .const_eval_global_id(param_env_reveal_all, cid, span) .expect("const_eval_global_id_for_typeck should have already failed"), @@ -682,8 +682,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // First try using a valtree in order to destructure the constant into a pattern. // FIXME: replace "try to do a thing, then fall back to another thing" // but something more principled, like a trait query checking whether this can be turned into a valtree. - if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) - { + if let Ok(Ok(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) { let subpattern = self.const_to_pat( Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), id, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f67518a577e2b..ade26a4092057 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -87,12 +87,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ) -> Option> { use rustc_middle::mir::interpret::ErrorHandled; match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { - Ok(Some(val)) => Some(ty::Const::new_value( + Ok(Ok(val)) => Some(ty::Const::new_value( self.tcx, val, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), )), - Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None, + Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None, Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())), } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 1d32ef2ccd94b..796f7fd5a54d1 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -765,8 +765,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { unevaluated, obligation.cause.span, ) { - Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), - Ok(None) => { + Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), + Ok(Err(_)) => { let tcx = self.tcx; let reported = tcx.dcx().emit_err(UnableToConstructConstantValue { diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 09225ac3246cb..6f5505e8a6387 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -235,7 +235,7 @@ impl<'tcx> NonCopyConst<'tcx> { fn is_value_unfrozen_raw( cx: &LateContext<'tcx>, - result: Result>, ErrorHandled>, + result: Result, Ty<'tcx>>, ErrorHandled>, ty: Ty<'tcx>, ) -> bool { result.map_or_else( From e613bc92a12c176d0e206b2f83b1bae8011e90c1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Jul 2024 17:24:50 +0200 Subject: [PATCH 2/5] const_to_pat: cleanup leftovers from when we had to deal with non-structural constants --- Cargo.lock | 1 + compiler/rustc_middle/src/ty/consts.rs | 52 +++-- compiler/rustc_mir_build/Cargo.toml | 1 + .../src/thir/pattern/const_to_pat.rs | 211 ++++++------------ .../rustc_mir_build/src/thir/pattern/mod.rs | 122 +++------- .../rustc_trait_selection/src/traits/mod.rs | 2 - .../src/traits/structural_match.rs | 173 -------------- .../const_in_pattern/custom-eq-branch-pass.rs | 11 + .../const_in_pattern/reject_non_partial_eq.rs | 2 +- .../reject_non_partial_eq.stderr | 5 +- .../invalid-inline-const-in-match-arm.rs | 1 + .../invalid-inline-const-in-match-arm.stderr | 8 +- ...-61188-match-slice-forbidden-without-eq.rs | 2 +- ...88-match-slice-forbidden-without-eq.stderr | 5 +- .../issue-6804-nan-match.rs | 4 - .../issue-6804-nan-match.stderr | 34 +-- 16 files changed, 174 insertions(+), 460 deletions(-) delete mode 100644 compiler/rustc_trait_selection/src/traits/structural_match.rs diff --git a/Cargo.lock b/Cargo.lock index 2d3bc59dddb21..1c1607e4c1ac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4411,6 +4411,7 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ + "either", "itertools", "rustc_apfloat", "rustc_arena", diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index d4c39a25a1f28..4d213d14af126 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,6 +1,7 @@ use crate::middle::resolve_bound_vars as rbv; use crate::mir::interpret::{ErrorHandled, LitToConstInput, Scalar}; use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use either::Either; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; use rustc_hir as hir; @@ -312,14 +313,16 @@ impl<'tcx> Const<'tcx> { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } - /// Returns the evaluated constant + /// Returns the evaluated constant as a valtree; + /// if that fails due to a valtree-incompatible type, indicate which type that is + /// by returning `Err(Left(bad_type))`. #[inline] - pub fn eval( + pub fn eval_valtree( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, span: Span, - ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { + ) -> Result<(Ty<'tcx>, ValTree<'tcx>), Either, ErrorHandled>> { assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); match self.kind() { ConstKind::Unevaluated(unevaluated) => { @@ -328,26 +331,47 @@ impl<'tcx> Const<'tcx> { let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let Ok(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? else { - // This can happen when we run on ill-typed code. - let e = tcx.dcx().span_delayed_bug( - span, - "`ty::Const::eval` called on a non-valtree-compatible type", - ); - return Err(e.into()); - }; - Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) + match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span) { + Ok(Ok(c)) => { + Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) + } + Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)), + Err(err) => Err(Either::Right(err.into())), + } } ConstKind::Value(ty, val) => Ok((ty, val)), - ConstKind::Error(g) => Err(g.into()), + ConstKind::Error(g) => Err(Either::Right(g.into())), ConstKind::Param(_) | ConstKind::Infer(_) | ConstKind::Bound(_, _) | ConstKind::Placeholder(_) - | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)), + | ConstKind::Expr(_) => Err(Either::Right(ErrorHandled::TooGeneric(span))), } } + /// Returns the evaluated constant + #[inline] + pub fn eval( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + span: Span, + ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { + self.eval_valtree(tcx, param_env, span).map_err(|err| { + match err { + Either::Right(err) => err, + Either::Left(_bad_ty) => { + // This can happen when we run on ill-typed code. + let e = tcx.dcx().span_delayed_bug( + span, + "`ty::Const::eval` called on a non-valtree-compatible type", + ); + e.into() + } + } + }) + } + /// Normalizes the constant to a value or an error if possible. #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 5d828d0093f3c..529e9cc271136 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +either = "1.5.0" itertools = "0.12" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } 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 5745dc0969cce..76efb6574e17b 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,42 +1,46 @@ +use either::Either; use rustc_apfloat::Float; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::mir; -use rustc_middle::span_bug; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TyCtxt, ValTree}; use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCause}; +use rustc_trait_selection::traits::ObligationCause; use tracing::{debug, instrument, trace}; use std::cell::Cell; use super::PatCtxt; use crate::errors::{ - InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, - UnsizedPattern, + ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern, + PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, }; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - /// Converts an evaluated constant to a pattern (if possible). + /// Converts a 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). /// - /// `cv` must be a valtree or a `mir::ConstValue`. + /// Only type system constants are supported, as we are using valtrees + /// as an intermediate step. Unfortunately those don't carry a type + /// so we have to carry one ourselves. #[instrument(level = "debug", skip(self), ret)] pub(super) fn const_to_pat( &self, - cv: mir::Const<'tcx>, + c: ty::Const<'tcx>, + ty: Ty<'tcx>, id: hir::HirId, span: Span, ) -> Box> { let infcx = self.tcx.infer_ctxt().build(); let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(cv) + convert.to_pat(c, ty) } } @@ -56,12 +60,6 @@ struct ConstToPat<'tcx> { treat_byte_string_as_slice: bool, } -/// This error type signals that we encountered a non-struct-eq situation. -/// We will fall back to calling `PartialEq::eq` on such patterns, -/// and exhaustiveness checking will consider them as matching nothing. -#[derive(Debug)] -struct FallbackToOpaqueConst; - impl<'tcx> ConstToPat<'tcx> { fn new( pat_ctxt: &PatCtxt<'_, 'tcx>, @@ -91,116 +89,55 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat(&mut self, cv: mir::Const<'tcx>) -> Box> { + fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>) -> Box> { 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 - // invoked except by this method. - // - // once indirect_structural_match is a full fledged error, this - // level of indirection can be eliminated + let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); - let have_valtree = - matches!(cv, mir::Const::Ty(_, c) if matches!(c.kind(), ty::ConstKind::Value(_, _))); - let inlined_const_as_pat = match cv { - mir::Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Param(_) - | ty::ConstKind::Infer(_) - | ty::ConstKind::Bound(_, _) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Error(_) - | ty::ConstKind::Expr(_) => { - span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind()) - } - ty::ConstKind::Value(ty, valtree) => { - self.recur(valtree, ty).unwrap_or_else(|_: FallbackToOpaqueConst| { - Box::new(Pat { - span: self.span, - ty: cv.ty(), - kind: PatKind::Constant { value: cv }, - }) - }) - } - }, - mir::Const::Unevaluated(_, _) => { - span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}") + // Get a valtree. If that fails, this const is definitely not valid for use as a pattern. + let valtree = match c.eval_valtree(self.tcx(), self.param_env, self.span) { + Ok((_, valtree)) => valtree, + Err(Either::Right(e)) => { + let err = match e { + ErrorHandled::Reported(..) => { + // Let's tell the use where this failing const occurs. + self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }) + } + ErrorHandled::TooGeneric(_) => self + .tcx() + .dcx() + .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }), + }; + return pat_from_kind(PatKind::Error(err)); + } + Err(Either::Left(bad_ty)) => { + // The pattern cannot be turned into a valtree. + let e = match bad_ty.kind() { + ty::Adt(def, ..) => { + assert!(def.is_union()); + self.tcx().dcx().emit_err(UnionPattern { span: self.span }) + } + ty::FnPtr(..) | ty::RawPtr(..) => { + self.tcx().dcx().emit_err(PointerPattern { span: self.span }) + } + _ => self + .tcx() + .dcx() + .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }), + }; + return pat_from_kind(PatKind::Error(e)); } - mir::Const::Val(_, _) => Box::new(Pat { - span: self.span, - ty: cv.ty(), - kind: PatKind::Constant { value: cv }, - }), }; - if self.saw_const_match_error.get().is_none() { - // If we were able to successfully convert the const to some pat (possibly with some - // lints, but no errors), double-check that all types in the const implement - // `PartialEq`. Even if we have a valtree, we may have found something - // in there with non-structural-equality, meaning we match using `PartialEq` - // and we hence have to check if that impl exists. - // This is all messy but not worth cleaning up: at some point we'll emit - // a hard error when we don't have a valtree or when we find something in - // the valtree that is not structural; then this can all be made a lot simpler. - - let structural = traits::search_for_structural_match_violation(self.tcx(), cv.ty()); - debug!( - "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", - cv.ty(), - structural - ); - - if let Some(non_sm_ty) = structural { - if !self.type_has_partial_eq_impl(cv.ty()) { - // This is reachable and important even if we have a valtree: there might be - // non-structural things in a valtree, in which case we fall back to `PartialEq` - // comparison, in which case we better make sure the trait is implemented for - // each inner type (and not just for the surrounding type). - let e = if let ty::Adt(def, ..) = non_sm_ty.kind() { - if def.is_union() { - let err = UnionPattern { span: self.span }; - self.tcx().dcx().emit_err(err) - } else { - // fatal avoids ICE from resolution of nonexistent method (rare case). - self.tcx() - .dcx() - .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty }) - } - } else { - let err = InvalidPattern { span: self.span, non_sm_ty }; - self.tcx().dcx().emit_err(err) - }; - // All branches above emitted an error. Don't print any more lints. - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } else if !have_valtree { - // Not being structural prevented us from constructing a valtree, - // so this is definitely a case we want to reject. - let err = TypeNotStructural { span: self.span, non_sm_ty }; - let e = self.tcx().dcx().emit_err(err); - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } else { - // This could be a violation in an inactive enum variant. - // Since we have a valtree, we trust that we have traversed the full valtree and - // complained about structural match violations there, so we don't - // have to check anything any more. - } - } else if !have_valtree { - // The only way valtree construction can fail without the structural match - // checker finding a violation is if there is a pointer somewhere. - let e = self.tcx().dcx().emit_err(PointerPattern { span: self.span }); - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } + // Convert the valtree to a const. + let inlined_const_as_pat = self.valtree_to_pat(valtree, ty); + if self.saw_const_match_error.get().is_none() { // Always check for `PartialEq` if we had no other errors yet. - if !self.type_has_partial_eq_impl(cv.ty()) { - let err = TypeNotPartialEq { span: self.span, non_peq_ty: cv.ty() }; + if !self.type_has_partial_eq_impl(ty) { + let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; let e = self.tcx().dcx().emit_err(err); let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); + return Box::new(Pat { span: self.span, ty: ty, kind }); } } @@ -243,36 +180,28 @@ impl<'tcx> ConstToPat<'tcx> { fn field_pats( &self, vals: impl Iterator, Ty<'tcx>)>, - ) -> Result>, FallbackToOpaqueConst> { + ) -> Vec> { vals.enumerate() .map(|(idx, (val, ty))| { let field = FieldIdx::new(idx); // Patterns can only use monomorphic types. let ty = self.tcx().normalize_erasing_regions(self.param_env, ty); - Ok(FieldPat { field, pattern: self.recur(val, ty)? }) + FieldPat { field, pattern: self.valtree_to_pat(val, ty) } }) .collect() } // Recursive helper for `to_pat`; invoke that (instead of calling this directly). #[instrument(skip(self), level = "debug")] - fn recur( - &self, - cv: ValTree<'tcx>, - ty: Ty<'tcx>, - ) -> Result>, FallbackToOpaqueConst> { + fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box> { let span = self.span; let tcx = self.tcx(); let param_env = self.param_env; let kind = match ty.kind() { - ty::FnDef(..) => { - let e = tcx.dcx().emit_err(InvalidPattern { span, non_sm_ty: ty }); - self.saw_const_match_error.set(Some(e)); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) - } ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => { + // Extremely important check for all ADTs! Make sure they opted-in to be used in + // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,); let err = TypeNotStructural { span, non_sm_ty: ty }; let e = tcx.dcx().emit_err(err); @@ -294,13 +223,9 @@ impl<'tcx> ConstToPat<'tcx> { .iter() .map(|field| field.ty(self.tcx(), args)), ), - )?, + ), } } - ty::Tuple(fields) => PatKind::Leaf { - subpatterns: self - .field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?, - }, ty::Adt(def, args) => { assert!(!def.is_union()); // Valtree construction would never succeed for unions. PatKind::Leaf { @@ -311,15 +236,18 @@ impl<'tcx> ConstToPat<'tcx> { .iter() .map(|field| field.ty(self.tcx(), args)), ), - )?, + ), } } + ty::Tuple(fields) => PatKind::Leaf { + subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())), + }, ty::Slice(elem_ty) => PatKind::Slice { prefix: cv .unwrap_branch() .iter() - .map(|val| self.recur(*val, *elem_ty)) - .collect::>()?, + .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .collect(), slice: None, suffix: Box::new([]), }, @@ -327,8 +255,8 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| self.recur(*val, *elem_ty)) - .collect::>()?, + .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .collect(), slice: None, suffix: Box::new([]), }, @@ -345,6 +273,7 @@ impl<'tcx> ConstToPat<'tcx> { if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() { let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; let e = tcx.dcx().emit_err(err); + self.saw_const_match_error.set(Some(e)); // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) } else { @@ -361,7 +290,7 @@ impl<'tcx> ConstToPat<'tcx> { _ => *pointee_ty, }; // References have the same valtree representation as their pointee. - let subpattern = self.recur(cv, pointee_ty)?; + let subpattern = self.valtree_to_pat(cv, pointee_ty); PatKind::Deref { subpattern } } } @@ -379,7 +308,7 @@ impl<'tcx> ConstToPat<'tcx> { // Also see . let e = tcx.dcx().emit_err(NaNPattern { span }); self.saw_const_match_error.set(Some(e)); - return Err(FallbackToOpaqueConst); + PatKind::Error(e) } else { PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), @@ -405,6 +334,6 @@ impl<'tcx> ConstToPat<'tcx> { } }; - Ok(Box::new(Pat { span, ty, kind })) + Box::new(Pat { span, ty, kind }) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 25b7d24de3979..553c5e175120f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -14,8 +14,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_index::Idx; use rustc_lint as lint; -use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput}; -use rustc_middle::mir::{self, Const}; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; @@ -575,63 +574,39 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } }; - let cid = GlobalId { instance, promoted: None }; - // Prefer valtrees over opaque constants. - let const_value = self - .tcx - .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) - .map(|val| match val { - Ok(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), - Err(_) => mir::Const::Val( - self.tcx - .const_eval_global_id(param_env_reveal_all, cid, span) - .expect("const_eval_global_id_for_typeck should have already failed"), - ty, - ), - }); - - match const_value { - Ok(const_) => { - let pattern = self.const_to_pat(const_, id, span); - - if !is_associated_const { - return pattern; - } + let c = ty::Const::new_unevaluated( + self.tcx, + ty::UnevaluatedConst { def: instance.def_id(), args: instance.args }, + ); - 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: Box::new(user_ty), - span, - inferred_ty: self.typeck_results().node_type(id), - }; - Box::new(Pat { - span, - kind: PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - annotation, - // Note that use `Contravariant` here. See the - // `variance` field documentation for details. - variance: ty::Contravariant, - }, - }, - ty: const_.ty(), - }) - } else { - pattern - } - } - 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. - let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span }); - pat_from_kind(PatKind::Error(e)) - } - Err(_) => { - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span }); - pat_from_kind(PatKind::Error(e)) - } + let pattern = self.const_to_pat(c, ty, id, span); + + if !is_associated_const { + return pattern; + } + + 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: Box::new(user_ty), + span, + inferred_ty: self.typeck_results().node_type(id), + }; + Box::new(Pat { + span, + kind: PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + annotation, + // Note that use `Contravariant` here. See the + // `variance` field documentation for details. + variance: ty::Contravariant, + }, + }, + ty, + }) + } else { + pattern } } @@ -662,7 +637,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; if let Some(lit_input) = lit_input { match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return self.const_to_pat(Const::Ty(ty, c), id, span).kind, + Ok(c) => return self.const_to_pat(c, ty, id, span).kind, // If an error occurred, ignore that it's a literal // and leave reporting the error up to const eval of // the unevaluated constant below. @@ -675,32 +650,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id)); let args = ty::InlineConstArgs::new(tcx, ty::InlineConstArgsParts { parent_args, ty }).args; - let uneval = mir::UnevaluatedConst { def: def_id.to_def_id(), args, promoted: None }; debug_assert!(!args.has_free_regions()); let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; - // First try using a valtree in order to destructure the constant into a pattern. - // FIXME: replace "try to do a thing, then fall back to another thing" - // but something more principled, like a trait query checking whether this can be turned into a valtree. - if let Ok(Ok(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) { - let subpattern = self.const_to_pat( - Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), - id, - span, - ); - PatKind::InlineConstant { subpattern, def: def_id } - } else { - // If that fails, convert it to an opaque constant pattern. - match tcx.const_eval_resolve(self.param_env, uneval, span) { - Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind, - Err(ErrorHandled::TooGeneric(_)) => { - // If we land here it means the const can't be evaluated because it's `TooGeneric`. - let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span }); - PatKind::Error(e) - } - Err(ErrorHandled::Reported(err, ..)) => PatKind::Error(err.into()), - } - } + let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); + PatKind::InlineConstant { subpattern, def: def_id } } /// Converts literals, paths and negation of literals to patterns. @@ -728,9 +682,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ct_ty = self.typeck_results.expr_ty(expr); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(constant) => { - self.const_to_pat(Const::Ty(ct_ty, constant), expr.hir_id, lit.span).kind - } + Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind, Err(LitToConstError::Reported(e)) => PatKind::Error(e), Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index d28982ed84925..f7eb173058299 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -16,7 +16,6 @@ pub mod query; #[allow(hidden_glob_reexports)] mod select; mod specialize; -mod structural_match; mod structural_normalize; #[allow(hidden_glob_reexports)] mod util; @@ -60,7 +59,6 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{ specialization_graph, translate_args, translate_args_with_cause, OverlapError, }; -pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo}; diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs deleted file mode 100644 index d4535db951e25..0000000000000 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ /dev/null @@ -1,173 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_middle::bug; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use std::ops::ControlFlow; - -/// This method traverses the structure of `ty`, trying to find an -/// instance of an ADT (i.e. struct or enum) that doesn't implement -/// the structural-match traits, or a generic type parameter -/// (which cannot be determined to be structural-match). -/// -/// The "structure of a type" includes all components that would be -/// considered when doing a pattern match on a constant of that -/// type. -/// -/// * This means this method descends into fields of structs/enums, -/// and also descends into the inner type `T` of `&T` and `&mut T` -/// -/// * The traversal doesn't dereference unsafe pointers (`*const T`, -/// `*mut T`), and it does not visit the type arguments of an -/// instantiated generic like `PhantomData`. -/// -/// The reason we do this search is Rust currently require all ADTs -/// reachable from a constant's type to implement the -/// structural-match traits, which essentially say that -/// the implementation of `PartialEq::eq` behaves *equivalently* to a -/// comparison against the unfolded structure. -/// -/// For more background on why Rust has this requirement, and issues -/// that arose when the requirement was not enforced completely, see -/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -pub fn search_for_structural_match_violation<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option> { - ty.visit_with(&mut Search { tcx, seen: FxHashSet::default() }).break_value() -} - -/// This implements the traversal over the structure of a given type to try to -/// find instances of ADTs (specifically structs or enums) that do not implement -/// `StructuralPartialEq`. -struct Search<'tcx> { - tcx: TyCtxt<'tcx>, - - /// Tracks ADTs previously encountered during search, so that - /// we will not recur on them again. - seen: FxHashSet, -} - -impl<'tcx> Search<'tcx> { - fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { - adt_ty.is_structural_eq_shallow(self.tcx) - } -} - -impl<'tcx> TypeVisitor> for Search<'tcx> { - type Result = ControlFlow>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - debug!("Search visiting ty: {:?}", ty); - - let (adt_def, args) = match *ty.kind() { - ty::Adt(adt_def, args) => (adt_def, args), - ty::Param(_) => { - return ControlFlow::Break(ty); - } - ty::Dynamic(..) => { - return ControlFlow::Break(ty); - } - ty::Foreign(_) => { - return ControlFlow::Break(ty); - } - ty::Alias(..) => { - return ControlFlow::Break(ty); - } - ty::Closure(..) => { - return ControlFlow::Break(ty); - } - ty::CoroutineClosure(..) => { - return ControlFlow::Break(ty); - } - ty::Coroutine(..) | ty::CoroutineWitness(..) => { - return ControlFlow::Break(ty); - } - ty::FnDef(..) => { - // Types of formals and return in `fn(_) -> _` are also irrelevant; - // so we do not recur into them via `super_visit_with` - return ControlFlow::Continue(()); - } - ty::Array(_, n) - if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } => - { - // rust-lang/rust#62336: ignore type of contents - // for empty array. - return ControlFlow::Continue(()); - } - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => { - // These primitive types are always structural match. - // - // `Never` is kind of special here, but as it is not inhabitable, this should be fine. - return ControlFlow::Continue(()); - } - - ty::FnPtr(..) => { - return ControlFlow::Continue(()); - } - - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::Continue(()); - } - - ty::Float(_) => { - return ControlFlow::Continue(()); - } - - ty::Pat(..) | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { - // First check all contained types and then tell the caller to continue searching. - return ty.super_visit_with(self); - } - ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { - bug!("unexpected type during structural-match checking: {:?}", ty); - } - ty::Error(_) => { - // We still want to check other types after encountering an error, - // as this may still emit relevant errors. - return ControlFlow::Continue(()); - } - }; - - if !self.seen.insert(adt_def.did()) { - debug!("Search already seen adt_def: {:?}", adt_def); - return ControlFlow::Continue(()); - } - - if !self.type_marked_structural(ty) { - debug!("Search found ty: {:?}", ty); - return ControlFlow::Break(ty); - } - - // structural-match does not care about the - // instantiation of the generics in an ADT (it - // instead looks directly at its fields outside - // this match), so we skip super_visit_with. - // - // (Must not recur on args for `PhantomData` cf - // rust-lang/rust#55028 and rust-lang/rust#55837; but also - // want to skip args when only uses of generic are - // behind unsafe pointers `*const T`/`*mut T`.) - - // even though we skip super_visit_with, we must recur on - // fields of ADT. - let tcx = self.tcx; - adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| { - let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); - debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); - ty.visit_with(self) - }) - } -} diff --git a/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs b/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs index 2e7061e7c4b49..ab8eec876bcbb 100644 --- a/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs +++ b/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs @@ -23,9 +23,20 @@ const BAR_BAZ: Foo = if 42 == 42 { Foo::Qux(CustomEq) // dead arm }; +const EMPTY: &[CustomEq] = &[]; + fn main() { + // BAR_BAZ itself is fine but the enum has other variants + // that are non-structural. Still, this should be accepted. match Foo::Qux(CustomEq) { BAR_BAZ => panic!(), _ => {} } + + // Similarly, an empty slice of a type that is non-structural + // is accepted. + match &[CustomEq] as &[CustomEq] { + EMPTY => panic!(), + _ => {}, + } } diff --git a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs index 86d971044fe1c..645e141891243 100644 --- a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs +++ b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs @@ -26,7 +26,7 @@ fn main() { match None { NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"), - //~^ ERROR must be annotated with `#[derive(PartialEq)]` + //~^ ERROR must implement `PartialEq` _ => panic!("whoops"), } } diff --git a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr index 88b82d5004b5c..ed531a1fead8d 100644 --- a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr +++ b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr @@ -1,11 +1,8 @@ -error: to use a constant of type `NoPartialEq` in a pattern, `NoPartialEq` must be annotated with `#[derive(PartialEq)]` +error: to use a constant of type `Option` in a pattern, the type must implement `PartialEq` --> $DIR/reject_non_partial_eq.rs:28:9 | LL | NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"), | ^^^^^^^^^^^^^^^^^^ - | - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: aborting due to 1 previous error diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.rs b/tests/ui/consts/invalid-inline-const-in-match-arm.rs index 0654fd82fbc59..4fe4b0d33c886 100644 --- a/tests/ui/consts/invalid-inline-const-in-match-arm.rs +++ b/tests/ui/consts/invalid-inline-const-in-match-arm.rs @@ -4,5 +4,6 @@ fn main() { match () { const { (|| {})() } => {} //~^ ERROR cannot call non-const closure in constants + //~| ERROR could not evaluate constant pattern } } diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr index 7579f7f969245..0e41053a29df0 100644 --- a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr +++ b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr @@ -11,6 +11,12 @@ help: add `#![feature(const_trait_impl)]` to the crate attributes to enable LL + #![feature(const_trait_impl)] | -error: aborting due to 1 previous error +error: could not evaluate constant pattern + --> $DIR/invalid-inline-const-in-match-arm.rs:5:9 + | +LL | const { (|| {})() } => {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs index c69fe145f77ba..c01f8934c750e 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs @@ -13,7 +13,7 @@ const A: &[B] = &[]; pub fn main() { match &[][..] { A => (), - //~^ ERROR must be annotated with `#[derive(PartialEq)]` + //~^ ERROR must implement `PartialEq` _ => (), } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr index 02e2bab4d0a29..736e4c30c8ae3 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr @@ -1,11 +1,8 @@ -error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` +error: to use a constant of type `&[B]` in a pattern, the type must implement `PartialEq` --> $DIR/issue-61188-match-slice-forbidden-without-eq.rs:15:9 | LL | A => (), | ^ - | - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs index 6d6a336e6885b..f343013f2b084 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs @@ -28,13 +28,9 @@ fn main() { // Also cover range patterns match x { NAN..=1.0 => {}, //~ ERROR cannot use NaN in patterns - //~^ ERROR lower range bound must be less than or equal to upper -1.0..=NAN => {}, //~ ERROR cannot use NaN in patterns - //~^ ERROR lower range bound must be less than or equal to upper NAN.. => {}, //~ ERROR cannot use NaN in patterns - //~^ ERROR lower range bound must be less than or equal to upper ..NAN => {}, //~ ERROR cannot use NaN in patterns - //~^ ERROR lower range bound must be less than upper _ => {}, }; } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr index baca1d75048ee..44b05ea31e962 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr @@ -34,14 +34,8 @@ LL | NAN..=1.0 => {}, = note: NaNs compare inequal to everything, even themselves, so this pattern would never match = help: try using the `is_nan` method instead -error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:30:9 - | -LL | NAN..=1.0 => {}, - | ^^^^^^^^^ lower bound larger than upper bound - error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:32:16 + --> $DIR/issue-6804-nan-match.rs:31:16 | LL | -1.0..=NAN => {}, | ^^^ @@ -49,14 +43,8 @@ LL | -1.0..=NAN => {}, = note: NaNs compare inequal to everything, even themselves, so this pattern would never match = help: try using the `is_nan` method instead -error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:32:9 - | -LL | -1.0..=NAN => {}, - | ^^^^^^^^^^ lower bound larger than upper bound - error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:34:9 + --> $DIR/issue-6804-nan-match.rs:32:9 | LL | NAN.. => {}, | ^^^ @@ -64,14 +52,8 @@ LL | NAN.. => {}, = note: NaNs compare inequal to everything, even themselves, so this pattern would never match = help: try using the `is_nan` method instead -error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:34:9 - | -LL | NAN.. => {}, - | ^^^^^ lower bound larger than upper bound - error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:36:11 + --> $DIR/issue-6804-nan-match.rs:33:11 | LL | ..NAN => {}, | ^^^ @@ -79,13 +61,5 @@ LL | ..NAN => {}, = note: NaNs compare inequal to everything, even themselves, so this pattern would never match = help: try using the `is_nan` method instead -error[E0579]: lower range bound must be less than upper - --> $DIR/issue-6804-nan-match.rs:36:9 - | -LL | ..NAN => {}, - | ^^^^^ - -error: aborting due to 11 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0030, E0579. -For more information about an error, try `rustc --explain E0030`. From 86ce911f90d9498b33738655c8cfed2f637c335a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Jul 2024 18:03:05 +0200 Subject: [PATCH 3/5] pattern lowering: make sure we never call user-defined PartialEq instances --- compiler/rustc_middle/src/thir.rs | 11 ++--- .../rustc_mir_build/src/build/matches/test.rs | 45 +++++++------------ compiler/rustc_pattern_analysis/src/rustc.rs | 18 ++++++-- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index c97af68c29e5f..b80d00719ee5e 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -783,16 +783,13 @@ pub enum PatKind<'tcx> { }, /// One of the following: - /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus - /// exhaustiveness checking will detect if you use the same string twice in different - /// patterns. + /// * `&str`/`&[u8]` (represented as a valtree), which will be handled as a string/slice pattern + /// and thus exhaustiveness checking will detect if you use the same string/slice twice in + /// different patterns. /// * integer, bool, char or float (represented as a valtree), which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. - /// * Opaque constants (represented as `mir::ConstValue`), that must not be matched - /// structurally. So anything that does not derive `PartialEq` and `Eq`. - /// - /// These are always compared with the matched place using (the semantics of) `PartialEq`. + /// * `String`, if `string_deref_patterns` is enabled. Constant { value: mir::Const<'tcx>, }, diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index d29874a5ad4ba..5aed2537750dd 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -144,7 +144,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { && tcx.is_lang_item(def.did(), LangItem::String) { if !tcx.features().string_deref_patterns { - bug!( + span_bug!( + test.span, "matching on `String` went through without enabling string_deref_patterns" ); } @@ -432,40 +433,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - match *ty.kind() { - ty::Ref(_, deref_ty, _) => ty = deref_ty, - _ => { - // non_scalar_compare called on non-reference type - let temp = self.temp(ty, source_info.span); - self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect)); - let ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, ty); - let ref_temp = self.temp(ref_ty, source_info.span); - - self.cfg.push_assign( - block, - source_info, - ref_temp, - Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, temp), - ); - expect = Operand::Move(ref_temp); - - let ref_temp = self.temp(ref_ty, source_info.span); - self.cfg.push_assign( - block, - source_info, - ref_temp, - Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, val), - ); - val = ref_temp; + // Figure out the type on which we are calling `PartialEq`. This involves an extra wrapping + // reference: we can only compare two `&T`, and then compare_ty will be `T`. + // Make sure that we do *not* call any user-defined code here. + // The only types that can end up here are string and byte literals, + // which have their comparison defined in `core`. + // (Interestingly this means that exhaustiveness analysis relies, for soundness, + // on the `PartialEq` impls for `str` and `[u8]` to b correct!) + let compare_ty = match *ty.kind() { + ty::Ref(_, deref_ty, _) + if deref_ty == self.tcx.types.str_ || deref_ty != self.tcx.types.u8 => + { + deref_ty } - } + _ => span_bug!(source_info.span, "invalid type for non-scalar compare: {}", ty), + }; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); let method = trait_method( self.tcx, eq_def_id, sym::eq, - self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [ty, ty]), + self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]), ); let bool_ty = self.tcx.types.bool; diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d4dd4dd858c28..d17ee8bff503e 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -462,7 +462,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // This is a box pattern. ty::Adt(adt, ..) if adt.is_box() => Struct, ty::Ref(..) => Ref, - _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), + _ => span_bug!( + pat.span, + "pattern has unexpected type: pat: {:?}, ty: {:?}", + pat.kind, + ty.inner() + ), }; } PatKind::DerefPattern { .. } => { @@ -518,7 +523,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) .collect(); } - _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), + _ => span_bug!( + pat.span, + "pattern has unexpected type: pat: {:?}, ty: {}", + pat.kind, + ty.inner() + ), } } PatKind::Constant { value } => { @@ -663,7 +673,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } } - _ => bug!("invalid type for range pattern: {}", ty.inner()), + _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()), }; fields = vec![]; arity = 0; @@ -674,7 +684,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { Some(length.eval_target_usize(cx.tcx, cx.param_env) as usize) } ty::Slice(_) => None, - _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", ty), + _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()), }; let kind = if slice.is_some() { SliceKind::VarLen(prefix.len(), suffix.len()) From 67c99d6338922d270108fc6390d3ad5224b3a7e1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Jul 2024 18:32:10 +0200 Subject: [PATCH 4/5] avoid creating an Instance only to immediately disassemble it again --- .../src/error_codes/E0158.md | 17 +++++----- compiler/rustc_mir_build/messages.ftl | 2 -- compiler/rustc_mir_build/src/errors.rs | 9 +---- .../rustc_mir_build/src/thir/pattern/mod.rs | 33 ++----------------- src/tools/tidy/src/issues.txt | 1 - ...ssociated-const-type-parameter-arms.stderr | 15 --------- ...ssociated-const-type-parameter-pattern.rs} | 10 ++++-- ...ciated-const-type-parameter-pattern.stderr | 27 +++++++++++++++ .../ui/consts/issue-73976-polymorphic.stderr | 5 +-- tests/ui/consts/issue-79137-toogeneric.stderr | 3 +- .../const-match-pat-generic.stderr | 5 +-- .../issue-68393-let-pat-assoc-constant.rs | 26 --------------- .../issue-68393-let-pat-assoc-constant.stderr | 15 --------- 13 files changed, 54 insertions(+), 114 deletions(-) delete mode 100644 tests/ui/associated-consts/associated-const-type-parameter-arms.stderr rename tests/ui/associated-consts/{associated-const-type-parameter-arms.rs => associated-const-type-parameter-pattern.rs} (50%) create mode 100644 tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr delete mode 100644 tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs delete mode 100644 tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0158.md b/compiler/rustc_error_codes/src/error_codes/E0158.md index 03b93d925c19a..c31f1e13beee4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0158.md +++ b/compiler/rustc_error_codes/src/error_codes/E0158.md @@ -1,5 +1,4 @@ -An associated `const`, `const` parameter or `static` has been referenced -in a pattern. +A generic parameter or `static` has been referenced in a pattern. Erroneous code example: @@ -15,25 +14,25 @@ trait Bar { fn test(arg: Foo) { match arg { - A::X => println!("A::X"), // error: E0158: associated consts cannot be - // referenced in patterns + A::X => println!("A::X"), // error: E0158: constant pattern depends + // on a generic parameter Foo::Two => println!("Two") } } ``` -Associated `const`s cannot be referenced in patterns because it is impossible +Generic parameters cannot be referenced in patterns because it is impossible for the compiler to prove exhaustiveness (that some pattern will always match). Take the above example, because Rust does type checking in the *generic* method, not the *monomorphized* specific instance. So because `Bar` could have -theoretically infinite implementations, there's no way to always be sure that +theoretically arbitrary implementations, there's no way to always be sure that `A::X` is `Foo::One`. So this code must be rejected. Even if code can be proven exhaustive by a programmer, the compiler cannot currently prove this. -The same holds true of `const` parameters and `static`s. +The same holds true of `static`s. -If you want to match against an associated `const`, `const` parameter or -`static` consider using a guard instead: +If you want to match against a `const` that depends on a generic parameter or a +`static`, consider using a guard instead: ``` trait Trait { diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 0c277811fdacf..281f3ef6ef35a 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -4,8 +4,6 @@ mir_build_already_borrowed = cannot borrow value as mutable because it is also b mir_build_already_mut_borrowed = cannot borrow value as immutable because it is also borrowed as mutable -mir_build_assoc_const_in_pattern = associated consts cannot be referenced in patterns - mir_build_bindings_with_variant_name = pattern binding `{$name}` is named the same as one of the variants of the type `{$ty_path}` .suggestion = to match on the variant, qualify the path diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 7c73d8a6d47d4..f6f443b64a63a 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -566,13 +566,6 @@ pub(crate) struct StaticInPattern { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(mir_build_assoc_const_in_pattern, code = E0158)] -pub(crate) struct AssocConstInPattern { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(mir_build_const_param_in_pattern, code = E0158)] pub(crate) struct ConstParamInPattern { @@ -597,7 +590,7 @@ pub(crate) struct UnreachablePattern { } #[derive(Diagnostic)] -#[diag(mir_build_const_pattern_depends_on_generic_parameter)] +#[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] pub(crate) span: Span, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 553c5e175120f..622651800f44c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -548,37 +548,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), }; - // 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); - // N.B. There is no guarantee that args 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 args = self - .tcx - .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_args(id)); - let instance = match ty::Instance::try_resolve(self.tcx, param_env_reveal_all, def_id, args) - { - Ok(Some(i)) => i, - Ok(None) => { - // It should be assoc consts if there's no error but we cannot resolve it. - debug_assert!(is_associated_const); - - let e = self.tcx.dcx().emit_err(AssocConstInPattern { span }); - return pat_from_kind(PatKind::Error(e)); - } - - Err(_) => { - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span }); - return pat_from_kind(PatKind::Error(e)); - } - }; - - let c = ty::Const::new_unevaluated( - self.tcx, - ty::UnevaluatedConst { def: instance.def_id(), args: instance.args }, - ); - + let args = self.typeck_results.node_args(id); + let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); let pattern = self.const_to_pat(c, ty, id, span); if !is_associated_const { diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 3c7284ce6db60..5731097770481 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -3449,7 +3449,6 @@ ui/pattern/issue-6449.rs ui/pattern/issue-66270-pat-struct-parser-recovery.rs ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs ui/pattern/issue-67776-match-same-name-enum-variant-refs.rs -ui/pattern/issue-68393-let-pat-assoc-constant.rs ui/pattern/issue-72565.rs ui/pattern/issue-72574-1.rs ui/pattern/issue-72574-2.rs diff --git a/tests/ui/associated-consts/associated-const-type-parameter-arms.stderr b/tests/ui/associated-consts/associated-const-type-parameter-arms.stderr deleted file mode 100644 index 1ccf9febd4bdc..0000000000000 --- a/tests/ui/associated-consts/associated-const-type-parameter-arms.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0158]: associated consts cannot be referenced in patterns - --> $DIR/associated-const-type-parameter-arms.rs:20:9 - | -LL | A::X => println!("A::X"), - | ^^^^ - -error[E0158]: associated consts cannot be referenced in patterns - --> $DIR/associated-const-type-parameter-arms.rs:22:9 - | -LL | B::X => println!("B::X"), - | ^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/associated-consts/associated-const-type-parameter-arms.rs b/tests/ui/associated-consts/associated-const-type-parameter-pattern.rs similarity index 50% rename from tests/ui/associated-consts/associated-const-type-parameter-arms.rs rename to tests/ui/associated-consts/associated-const-type-parameter-pattern.rs index 3f260d84e4c0a..b5798adc71c86 100644 --- a/tests/ui/associated-consts/associated-const-type-parameter-arms.rs +++ b/tests/ui/associated-consts/associated-const-type-parameter-pattern.rs @@ -18,12 +18,18 @@ impl Foo for Def { pub fn test(arg: EFoo) { match arg { A::X => println!("A::X"), - //~^ error: associated consts cannot be referenced in patterns [E0158] + //~^ error: constant pattern depends on a generic parameter B::X => println!("B::X"), - //~^ error: associated consts cannot be referenced in patterns [E0158] + //~^ error: constant pattern depends on a generic parameter _ => (), } } +pub fn test_let_pat(arg: EFoo, A::X: EFoo) { + //~^ ERROR constant pattern depends on a generic parameter + let A::X = arg; + //~^ ERROR constant pattern depends on a generic parameter +} + fn main() { } diff --git a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr new file mode 100644 index 0000000000000..adc8f3992072d --- /dev/null +++ b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr @@ -0,0 +1,27 @@ +error[E0158]: constant pattern depends on a generic parameter + --> $DIR/associated-const-type-parameter-pattern.rs:20:9 + | +LL | A::X => println!("A::X"), + | ^^^^ + +error[E0158]: constant pattern depends on a generic parameter + --> $DIR/associated-const-type-parameter-pattern.rs:22:9 + | +LL | B::X => println!("B::X"), + | ^^^^ + +error[E0158]: constant pattern depends on a generic parameter + --> $DIR/associated-const-type-parameter-pattern.rs:30:9 + | +LL | let A::X = arg; + | ^^^^ + +error[E0158]: constant pattern depends on a generic parameter + --> $DIR/associated-const-type-parameter-pattern.rs:28:48 + | +LL | pub fn test_let_pat(arg: EFoo, A::X: EFoo) { + | ^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/consts/issue-73976-polymorphic.stderr b/tests/ui/consts/issue-73976-polymorphic.stderr index 97a5fbc5747a3..8a44eb9854fef 100644 --- a/tests/ui/consts/issue-73976-polymorphic.stderr +++ b/tests/ui/consts/issue-73976-polymorphic.stderr @@ -1,10 +1,10 @@ -error: constant pattern depends on a generic parameter +error[E0158]: constant pattern depends on a generic parameter --> $DIR/issue-73976-polymorphic.rs:20:37 | LL | matches!(GetTypeId::::VALUE, GetTypeId::::VALUE) | ^^^^^^^^^^^^^^^^^^^^^ -error: constant pattern depends on a generic parameter +error[E0158]: constant pattern depends on a generic parameter --> $DIR/issue-73976-polymorphic.rs:31:42 | LL | matches!(GetTypeNameLen::::VALUE, GetTypeNameLen::::VALUE) @@ -12,3 +12,4 @@ LL | matches!(GetTypeNameLen::::VALUE, GetTypeNameLen::::VALUE) error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/consts/issue-79137-toogeneric.stderr b/tests/ui/consts/issue-79137-toogeneric.stderr index 18bdde45e2c9d..de81512ec6d2c 100644 --- a/tests/ui/consts/issue-79137-toogeneric.stderr +++ b/tests/ui/consts/issue-79137-toogeneric.stderr @@ -1,4 +1,4 @@ -error: constant pattern depends on a generic parameter +error[E0158]: constant pattern depends on a generic parameter --> $DIR/issue-79137-toogeneric.rs:12:43 | LL | matches!(GetVariantCount::::VALUE, GetVariantCount::::VALUE) @@ -6,3 +6,4 @@ LL | matches!(GetVariantCount::::VALUE, GetVariantCount::::VALUE) error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/inline-const/const-match-pat-generic.stderr b/tests/ui/inline-const/const-match-pat-generic.stderr index 15c3a876afcf7..26f72b34eca29 100644 --- a/tests/ui/inline-const/const-match-pat-generic.stderr +++ b/tests/ui/inline-const/const-match-pat-generic.stderr @@ -1,10 +1,10 @@ -error: constant pattern depends on a generic parameter +error[E0158]: constant pattern depends on a generic parameter --> $DIR/const-match-pat-generic.rs:7:9 | LL | const { V } => {}, | ^^^^^^^^^^^ -error: constant pattern depends on a generic parameter +error[E0158]: constant pattern depends on a generic parameter --> $DIR/const-match-pat-generic.rs:19:9 | LL | const { f(V) } => {}, @@ -12,3 +12,4 @@ LL | const { f(V) } => {}, error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs b/tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs deleted file mode 100644 index 95ead6b5d4a61..0000000000000 --- a/tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub enum EFoo { - A, -} - -pub trait Foo { - const X: EFoo; -} - -struct Abc; - -impl Foo for Abc { - const X: EFoo = EFoo::A; -} - -struct Def; -impl Foo for Def { - const X: EFoo = EFoo::A; -} - -pub fn test(arg: EFoo, A::X: EFoo) { - //~^ ERROR associated consts cannot be referenced in patterns - let A::X = arg; - //~^ ERROR associated consts cannot be referenced in patterns -} - -fn main() {} diff --git a/tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr b/tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr deleted file mode 100644 index 62c90b638d7c3..0000000000000 --- a/tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0158]: associated consts cannot be referenced in patterns - --> $DIR/issue-68393-let-pat-assoc-constant.rs:22:9 - | -LL | let A::X = arg; - | ^^^^ - -error[E0158]: associated consts cannot be referenced in patterns - --> $DIR/issue-68393-let-pat-assoc-constant.rs:20:40 - | -LL | pub fn test(arg: EFoo, A::X: EFoo) { - | ^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0158`. From 303a2db2360e332a29a66de94fa65cdb52b0d51c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 15 Jul 2024 22:11:21 +0200 Subject: [PATCH 5/5] remove saw_const_match_error; check if pattern contains an Error instead --- .../src/thir/pattern/const_to_pat.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) 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 76efb6574e17b..0d54f332585aa 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 @@ -7,15 +7,14 @@ use rustc_infer::traits::Obligation; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt, ValTree}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; use tracing::{debug, instrument, trace}; -use std::cell::Cell; - use super::PatCtxt; use crate::errors::{ ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern, @@ -49,11 +48,6 @@ struct ConstToPat<'tcx> { span: Span, param_env: ty::ParamEnv<'tcx>, - // This tracks if we emitted some hard error for a given const value, so that - // we will not subsequently issue an irrelevant lint for the same const - // value. - saw_const_match_error: Cell>, - // inference context used for checking `T: Structural` bounds. infcx: InferCtxt<'tcx>, @@ -73,7 +67,6 @@ impl<'tcx> ConstToPat<'tcx> { span, infcx, param_env: pat_ctxt.param_env, - saw_const_match_error: Cell::new(None), treat_byte_string_as_slice: pat_ctxt .typeck_results .treat_byte_string_as_slice @@ -131,7 +124,7 @@ impl<'tcx> ConstToPat<'tcx> { // Convert the valtree to a const. let inlined_const_as_pat = self.valtree_to_pat(valtree, ty); - if self.saw_const_match_error.get().is_none() { + if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. if !self.type_has_partial_eq_impl(ty) { let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; @@ -205,7 +198,6 @@ impl<'tcx> ConstToPat<'tcx> { debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,); let err = TypeNotStructural { span, non_sm_ty: ty }; let e = tcx.dcx().emit_err(err); - self.saw_const_match_error.set(Some(e)); // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) } @@ -273,7 +265,6 @@ impl<'tcx> ConstToPat<'tcx> { if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() { let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; let e = tcx.dcx().emit_err(err); - self.saw_const_match_error.set(Some(e)); // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) } else { @@ -307,7 +298,6 @@ impl<'tcx> ConstToPat<'tcx> { // NaNs are not ever equal to anything so they make no sense as patterns. // Also see . let e = tcx.dcx().emit_err(NaNPattern { span }); - self.saw_const_match_error.set(Some(e)); PatKind::Error(e) } else { PatKind::Constant { @@ -328,7 +318,6 @@ impl<'tcx> ConstToPat<'tcx> { _ => { let err = InvalidPattern { span, non_sm_ty: ty }; let e = tcx.dcx().emit_err(err); - self.saw_const_match_error.set(Some(e)); // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) }