1
Fork 0

Rollup merge of #127687 - RalfJung:pattern-cleanup, r=oli-obk,lcnr

Const-to-pattern-to-MIR cleanup

Now that all uses of constants without structural equality are hard errors, there's a bunch of cleanup we can do in the code that handles patterns: we can always funnel patterns through valtrees first (rather than having a fallback path for when valtree construction fails), and we can make sure that if we emit a `PartialEq` call it is not calling anything user-defined.

To keep the error messages the same, I made valtree construction failures return the information of *which* type it is that cannot be valtree'd. `search_for_structural_match_violation` is now not needed any more at all, so I removed it.

r? `@oli-obk`
This commit is contained in:
Trevor Gross 2024-07-18 05:14:05 -05:00 committed by GitHub
commit a2178dffc8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 292 additions and 651 deletions

View file

@ -4411,6 +4411,7 @@ dependencies = [
name = "rustc_mir_build"
version = "0.0.0"
dependencies = [
"either",
"itertools",
"rustc_apfloat",
"rustc_arena",

View file

@ -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<Option<ty::ValTree<'tcx>>, ErrorHandled> {
) -> Result<Result<ty::ValTree<'tcx>, 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();

View file

@ -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<ty::ValTree<'tcx>, ValTreeCreationError>;
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError<'tcx>>;
impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
fn from(err: InterpErrorInfo<'_>) -> Self {
impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> {
fn from(err: InterpErrorInfo<'tcx>) -> Self {
ty::tls::with(|tcx| {
bug!(
"Unexpected Undefined Behavior error during valtree construction: {}",

View file

@ -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)),
}
}
}

View file

@ -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<A: Bar>(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 {

View file

@ -1427,17 +1427,17 @@ impl<'tcx> InferCtxt<'tcx> {
span: Span,
) -> Result<ty::Const<'tcx>, 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
);
}

View file

@ -90,9 +90,11 @@ TrivialTypeTraversalImpls! { ErrorHandled }
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, 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<Option<ValTree<'tcx>>, 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<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);

View file

@ -157,9 +157,10 @@ impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
}
impl EraseType for Result<Option<ty::ValTree<'_>>, mir::interpret::ErrorHandled> {
type Result =
[u8; size_of::<Result<Option<ty::ValTree<'static>>, mir::interpret::ErrorHandled>>()];
impl EraseType for Result<Result<ty::ValTree<'_>, Ty<'_>>, mir::interpret::ErrorHandled> {
type Result = [u8; size_of::<
Result<Result<ty::ValTree<'static>, Ty<'static>>, mir::interpret::ErrorHandled>,
>()];
}
impl EraseType for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {

View file

@ -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>,
},

View file

@ -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<Ty<'tcx>, ErrorHandled>> {
assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
match self.kind() {
ConstKind::Unevaluated(unevaluated) => {
@ -328,25 +331,45 @@ 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 {
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(Either::Right(g.into())),
ConstKind::Param(_)
| ConstKind::Infer(_)
| ConstKind::Bound(_, _)
| ConstKind::Placeholder(_)
| 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",
);
return Err(e.into());
};
Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c))
e.into()
}
}
ConstKind::Value(ty, val) => Ok((ty, val)),
ConstKind::Error(g) => Err(g.into()),
ConstKind::Param(_)
| ConstKind::Infer(_)
| ConstKind::Bound(_, _)
| ConstKind::Placeholder(_)
| ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)),
}
})
}
/// Normalizes the constant to a value or an error if possible.

View file

@ -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" }

View file

@ -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

View file

@ -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;

View file

@ -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,

View file

@ -1,42 +1,45 @@
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::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::{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<Pat<'tcx>> {
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)
}
}
@ -45,23 +48,12 @@ 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<Option<ErrorGuaranteed>>,
// inference context used for checking `T: Structural` bounds.
infcx: InferCtxt<'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>,
@ -75,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
@ -91,116 +82,55 @@ impl<'tcx> ConstToPat<'tcx> {
ty.is_structural_eq_shallow(self.infcx.tcx)
}
fn to_pat(&mut self, cv: mir::Const<'tcx>) -> Box<Pat<'tcx>> {
fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'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
// 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 !inlined_const_as_pat.references_error() {
// 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,40 +173,31 @@ impl<'tcx> ConstToPat<'tcx> {
fn field_pats(
&self,
vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
) -> Result<Vec<FieldPat<'tcx>>, FallbackToOpaqueConst> {
) -> Vec<FieldPat<'tcx>> {
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<Box<Pat<'tcx>>, FallbackToOpaqueConst> {
fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
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);
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)
}
@ -294,13 +215,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 +228,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::<Result<_, _>>()?,
.map(|val| self.valtree_to_pat(*val, *elem_ty))
.collect(),
slice: None,
suffix: Box::new([]),
},
@ -327,8 +247,8 @@ impl<'tcx> ConstToPat<'tcx> {
prefix: cv
.unwrap_branch()
.iter()
.map(|val| self.recur(*val, *elem_ty))
.collect::<Result<_, _>>()?,
.map(|val| self.valtree_to_pat(*val, *elem_ty))
.collect(),
slice: None,
suffix: Box::new([]),
},
@ -361,7 +281,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 }
}
}
@ -378,8 +298,7 @@ impl<'tcx> ConstToPat<'tcx> {
// NaNs are not ever equal to anything so they make no sense as patterns.
// Also see <https://github.com/rust-lang/rfcs/pull/3535>.
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)),
@ -399,12 +318,11 @@ 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)
}
};
Ok(Box::new(Pat { span, ty, kind }))
Box::new(Pat { span, ty, kind })
}
}

View file

@ -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,
};
@ -549,89 +548,36 @@ 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 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);
let e = self.tcx.dcx().emit_err(AssocConstInPattern { span });
return pat_from_kind(PatKind::Error(e));
}
if !is_associated_const {
return pattern;
}
Err(_) => {
let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span });
return pat_from_kind(PatKind::Error(e));
}
};
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 {
Some(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
None => 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 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 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 +608,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,33 +621,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(Some(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.
@ -729,9 +653,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"),
}

View file

@ -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())

View file

@ -87,12 +87,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
) -> Option<ty::Const<'tcx>> {
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())),
}
}

View file

@ -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 {

View file

@ -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};

View file

@ -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<T>`.
///
/// 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<'tcx>> {
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<hir::def_id::DefId>,
}
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<TyCtxt<'tcx>> for Search<'tcx> {
type Result = ControlFlow<Ty<'tcx>>;
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<T>` 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)
})
}
}

View file

@ -235,7 +235,7 @@ impl<'tcx> NonCopyConst<'tcx> {
fn is_value_unfrozen_raw(
cx: &LateContext<'tcx>,
result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
result: Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>,
ty: Ty<'tcx>,
) -> bool {
result.map_or_else(

View file

@ -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

View file

@ -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`.

View file

@ -18,12 +18,18 @@ impl Foo for Def {
pub fn test<A: Foo, B: Foo>(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<A: Foo, B: Foo>(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() {
}

View file

@ -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<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
| ^^^^
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0158`.

View file

@ -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!(),
_ => {},
}
}

View file

@ -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"),
}
}

View file

@ -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<NoPartialEq>` 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

View file

@ -4,5 +4,6 @@ fn main() {
match () {
const { (|| {})() } => {}
//~^ ERROR cannot call non-const closure in constants
//~| ERROR could not evaluate constant pattern
}
}

View file

@ -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`.

View file

@ -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::<T>::VALUE, GetTypeId::<T>::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::<T>::VALUE, GetTypeNameLen::<T>::VALUE)
@ -12,3 +12,4 @@ LL | matches!(GetTypeNameLen::<T>::VALUE, GetTypeNameLen::<T>::VALUE)
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0158`.

View file

@ -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::<T>::VALUE, GetVariantCount::<T>::VALUE)
@ -6,3 +6,4 @@ LL | matches!(GetVariantCount::<T>::VALUE, GetVariantCount::<T>::VALUE)
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0158`.

View file

@ -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`.

View file

@ -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<A: Foo, B: Foo>(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() {}

View file

@ -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<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
| ^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0158`.

View file

@ -13,7 +13,7 @@ const A: &[B] = &[];
pub fn main() {
match &[][..] {
A => (),
//~^ ERROR must be annotated with `#[derive(PartialEq)]`
//~^ ERROR must implement `PartialEq`
_ => (),
}
}

View file

@ -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

View file

@ -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
_ => {},
};
}

View file

@ -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 7 previous errors
error: aborting due to 11 previous errors
Some errors have detailed explanations: E0030, E0579.
For more information about an error, try `rustc --explain E0030`.