const_to_pat: cleanup leftovers from when we had to deal with non-structural constants
This commit is contained in:
parent
fa74a9e6aa
commit
e613bc92a1
@ -4411,6 +4411,7 @@ dependencies = [
|
||||
name = "rustc_mir_build"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools",
|
||||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
|
@ -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 @@ pub fn from_target_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
|
||||
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,24 +331,45 @@ pub fn eval(
|
||||
let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env);
|
||||
// try to resolve e.g. associated constants to their definition on an impl, and then
|
||||
// evaluate the const.
|
||||
let Ok(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? else {
|
||||
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" }
|
||||
|
@ -1,42 +1,46 @@
|
||||
use either::Either;
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_hir as hir;
|
||||
use rustc_index::Idx;
|
||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||
use rustc_trait_selection::traits::ObligationCause;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use super::PatCtxt;
|
||||
use crate::errors::{
|
||||
InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern,
|
||||
UnsizedPattern,
|
||||
ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
|
||||
PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
|
||||
};
|
||||
|
||||
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
/// Converts an evaluated constant to a pattern (if possible).
|
||||
/// Converts a constant to a pattern (if possible).
|
||||
/// This means aggregate values (like structs and enums) are converted
|
||||
/// to a pattern that matches the value (as if you'd compared via structural equality).
|
||||
///
|
||||
/// `cv` must be a valtree or a `mir::ConstValue`.
|
||||
/// Only type system constants are supported, as we are using valtrees
|
||||
/// as an intermediate step. Unfortunately those don't carry a type
|
||||
/// so we have to carry one ourselves.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub(super) fn const_to_pat(
|
||||
&self,
|
||||
cv: mir::Const<'tcx>,
|
||||
c: ty::Const<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
id: hir::HirId,
|
||||
span: Span,
|
||||
) -> Box<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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,12 +60,6 @@ struct ConstToPat<'tcx> {
|
||||
treat_byte_string_as_slice: bool,
|
||||
}
|
||||
|
||||
/// This error type signals that we encountered a non-struct-eq situation.
|
||||
/// We will fall back to calling `PartialEq::eq` on such patterns,
|
||||
/// and exhaustiveness checking will consider them as matching nothing.
|
||||
#[derive(Debug)]
|
||||
struct FallbackToOpaqueConst;
|
||||
|
||||
impl<'tcx> ConstToPat<'tcx> {
|
||||
fn new(
|
||||
pat_ctxt: &PatCtxt<'_, 'tcx>,
|
||||
@ -91,116 +89,55 @@ fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
|
||||
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 },
|
||||
}),
|
||||
};
|
||||
|
||||
// Convert the valtree to a const.
|
||||
let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
|
||||
|
||||
if self.saw_const_match_error.get().is_none() {
|
||||
// If 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 });
|
||||
}
|
||||
|
||||
// Always check for `PartialEq` if we had no other errors yet.
|
||||
if !self.type_has_partial_eq_impl(cv.ty()) {
|
||||
let err = TypeNotPartialEq { span: self.span, non_peq_ty: cv.ty() };
|
||||
if !self.type_has_partial_eq_impl(ty) {
|
||||
let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty };
|
||||
let e = self.tcx().dcx().emit_err(err);
|
||||
let kind = PatKind::Error(e);
|
||||
return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
|
||||
return Box::new(Pat { span: self.span, ty: ty, kind });
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,36 +180,28 @@ fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
|
||||
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);
|
||||
@ -294,13 +223,9 @@ fn recur(
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx(), args)),
|
||||
),
|
||||
)?,
|
||||
),
|
||||
}
|
||||
}
|
||||
ty::Tuple(fields) => PatKind::Leaf {
|
||||
subpatterns: self
|
||||
.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?,
|
||||
},
|
||||
ty::Adt(def, args) => {
|
||||
assert!(!def.is_union()); // Valtree construction would never succeed for unions.
|
||||
PatKind::Leaf {
|
||||
@ -311,15 +236,18 @@ fn recur(
|
||||
.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 +255,8 @@ fn recur(
|
||||
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([]),
|
||||
},
|
||||
@ -345,6 +273,7 @@ fn recur(
|
||||
if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
|
||||
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
|
||||
let e = tcx.dcx().emit_err(err);
|
||||
self.saw_const_match_error.set(Some(e));
|
||||
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
PatKind::Error(e)
|
||||
} else {
|
||||
@ -361,7 +290,7 @@ fn recur(
|
||||
_ => *pointee_ty,
|
||||
};
|
||||
// References have the same valtree representation as their pointee.
|
||||
let subpattern = self.recur(cv, pointee_ty)?;
|
||||
let subpattern = self.valtree_to_pat(cv, pointee_ty);
|
||||
PatKind::Deref { subpattern }
|
||||
}
|
||||
}
|
||||
@ -379,7 +308,7 @@ fn recur(
|
||||
// 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)),
|
||||
@ -405,6 +334,6 @@ fn recur(
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Box::new(Pat { span, ty, kind }))
|
||||
Box::new(Pat { span, ty, kind })
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,7 @@
|
||||
use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_lint as lint;
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput};
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
|
||||
use rustc_middle::thir::{
|
||||
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
|
||||
};
|
||||
@ -575,63 +574,39 @@ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) ->
|
||||
}
|
||||
};
|
||||
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
// Prefer valtrees over opaque constants.
|
||||
let const_value = self
|
||||
.tcx
|
||||
.const_eval_global_id_for_typeck(param_env_reveal_all, cid, span)
|
||||
.map(|val| match val {
|
||||
Ok(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
|
||||
Err(_) => mir::Const::Val(
|
||||
self.tcx
|
||||
.const_eval_global_id(param_env_reveal_all, cid, span)
|
||||
.expect("const_eval_global_id_for_typeck should have already failed"),
|
||||
ty,
|
||||
),
|
||||
});
|
||||
let c = ty::Const::new_unevaluated(
|
||||
self.tcx,
|
||||
ty::UnevaluatedConst { def: instance.def_id(), args: instance.args },
|
||||
);
|
||||
|
||||
match const_value {
|
||||
Ok(const_) => {
|
||||
let pattern = self.const_to_pat(const_, id, span);
|
||||
let pattern = self.const_to_pat(c, ty, id, span);
|
||||
|
||||
if !is_associated_const {
|
||||
return pattern;
|
||||
}
|
||||
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 +637,7 @@ fn lower_inline_const(
|
||||
};
|
||||
if let Some(lit_input) = lit_input {
|
||||
match tcx.at(expr.span).lit_to_const(lit_input) {
|
||||
Ok(c) => return self.const_to_pat(Const::Ty(ty, c), id, span).kind,
|
||||
Ok(c) => return self.const_to_pat(c, ty, id, span).kind,
|
||||
// If an error occurred, ignore that it's a literal
|
||||
// and leave reporting the error up to const eval of
|
||||
// the unevaluated constant below.
|
||||
@ -675,32 +650,11 @@ fn lower_inline_const(
|
||||
tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id));
|
||||
let args = ty::InlineConstArgs::new(tcx, ty::InlineConstArgsParts { parent_args, ty }).args;
|
||||
|
||||
let uneval = mir::UnevaluatedConst { def: def_id.to_def_id(), args, promoted: None };
|
||||
debug_assert!(!args.has_free_regions());
|
||||
|
||||
let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args };
|
||||
// First try using a valtree in order to destructure the constant into a pattern.
|
||||
// FIXME: replace "try to do a thing, then fall back to another thing"
|
||||
// but something more principled, like a trait query checking whether this can be turned into a valtree.
|
||||
if let Ok(Ok(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) {
|
||||
let subpattern = self.const_to_pat(
|
||||
Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
|
||||
id,
|
||||
span,
|
||||
);
|
||||
PatKind::InlineConstant { subpattern, def: def_id }
|
||||
} else {
|
||||
// If that fails, convert it to an opaque constant pattern.
|
||||
match tcx.const_eval_resolve(self.param_env, uneval, span) {
|
||||
Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind,
|
||||
Err(ErrorHandled::TooGeneric(_)) => {
|
||||
// If we land here it means the const can't be evaluated because it's `TooGeneric`.
|
||||
let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span });
|
||||
PatKind::Error(e)
|
||||
}
|
||||
Err(ErrorHandled::Reported(err, ..)) => PatKind::Error(err.into()),
|
||||
}
|
||||
}
|
||||
let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span);
|
||||
PatKind::InlineConstant { subpattern, def: def_id }
|
||||
}
|
||||
|
||||
/// Converts literals, paths and negation of literals to patterns.
|
||||
@ -728,9 +682,7 @@ fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'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"),
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
#[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, 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -23,9 +23,20 @@ enum Foo {
|
||||
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`.
|
||||
|
@ -13,7 +13,7 @@
|
||||
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…
Reference in New Issue
Block a user