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:
commit
a2178dffc8
40 changed files with 292 additions and 651 deletions
|
@ -4411,6 +4411,7 @@ dependencies = [
|
|||
name = "rustc_mir_build"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools",
|
||||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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: {}",
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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>,
|
||||
},
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
|
@ -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() {
|
||||
}
|
|
@ -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`.
|
|
@ -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!(),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -4,5 +4,6 @@ fn main() {
|
|||
match () {
|
||||
const { (|| {})() } => {}
|
||||
//~^ ERROR cannot call non-const closure in constants
|
||||
//~| ERROR could not evaluate constant pattern
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -13,7 +13,7 @@ const A: &[B] = &[];
|
|||
pub fn main() {
|
||||
match &[][..] {
|
||||
A => (),
|
||||
//~^ ERROR must be annotated with `#[derive(PartialEq)]`
|
||||
//~^ ERROR must implement `PartialEq`
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue