Auto merge of #111913 - oli-obk:valtrees2, r=lcnr
Only rewrite valtree-constants to patterns and keep other constants opaque Now that we can reliably fall back to comparing constants with `PartialEq::eq` to the match scrutinee, we can 1. eagerly try to convert constants to valtrees 2. then deeply convert the valtree to a pattern 3. if the to-valtree conversion failed, create an "opaque constant" pattern. This PR specifically avoids any behavioral changes or major cleanups. What we can now do as follow ups is * move the two remaining call sites to `destructure_mir_constant` off that query * make valtree to pattern conversion infallible * this needs to be done after careful analysis of the effects. There may be user visible changes from that. based on https://github.com/rust-lang/rust/pull/111768
This commit is contained in:
commit
871b595202
@ -2,10 +2,8 @@
|
||||
|
||||
use crate::errors::MaxNumNodesInConstErr;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
|
||||
Scalar,
|
||||
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, Scalar,
|
||||
};
|
||||
use rustc_hir::Mutability;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
@ -131,38 +129,3 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
|
||||
|
||||
Ok(mir::DestructuredConstant { variant, fields })
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub(crate) fn deref_mir_constant<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
val: mir::ConstantKind<'tcx>,
|
||||
) -> mir::ConstantKind<'tcx> {
|
||||
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
|
||||
let op = ecx.eval_mir_constant(&val, None, None).unwrap();
|
||||
let mplace = ecx.deref_operand(&op).unwrap();
|
||||
if let Some(alloc_id) = mplace.ptr.provenance {
|
||||
assert_eq!(
|
||||
tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability,
|
||||
Mutability::Not,
|
||||
"deref_mir_constant cannot be used with mutable allocations as \
|
||||
that could allow pattern matching to observe mutable statics",
|
||||
);
|
||||
}
|
||||
|
||||
let ty = match mplace.meta {
|
||||
MemPlaceMeta::None => mplace.layout.ty,
|
||||
// In case of unsized types, figure out the real type behind.
|
||||
MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
|
||||
ty::Str => bug!("there's no sized equivalent of a `str`"),
|
||||
ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_target_usize(&tcx).unwrap()),
|
||||
_ => bug!(
|
||||
"type {} should not have metadata, but had {:?}",
|
||||
mplace.layout.ty,
|
||||
mplace.meta
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
mir::ConstantKind::Val(op_to_const(&ecx, &mplace.into()), ty)
|
||||
}
|
||||
|
@ -56,10 +56,6 @@ pub fn provide(providers: &mut Providers) {
|
||||
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
|
||||
const_eval::valtree_to_const_value(tcx, ty::ParamEnv::empty().and(ty), valtree)
|
||||
};
|
||||
providers.deref_mir_constant = |tcx, param_env_and_value| {
|
||||
let (param_env, value) = param_env_and_value.into_parts();
|
||||
const_eval::deref_mir_constant(tcx, param_env, value)
|
||||
};
|
||||
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
|
||||
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
|
||||
};
|
||||
|
@ -439,7 +439,7 @@ pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
|
||||
/// Given the metadata, extract out the value at a particular index (if any).
|
||||
#[inline(never)]
|
||||
pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
|
||||
debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
|
||||
trace!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
|
||||
|
||||
let start = self.position.get();
|
||||
let bytes = &metadata.blob()[start..start + self.encoded_size];
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
|
||||
|
||||
use crate::mir::interpret::{
|
||||
AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, LitToConstInput, Scalar,
|
||||
AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
|
||||
};
|
||||
use crate::mir::visit::MirVisitable;
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
@ -2461,51 +2461,6 @@ pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
|
||||
Self::Val(val, ty)
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug", ret)]
|
||||
pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let body_id = match tcx.hir().get(hir_id) {
|
||||
hir::Node::AnonConst(ac) => ac.body,
|
||||
_ => span_bug!(
|
||||
tcx.def_span(def_id.to_def_id()),
|
||||
"from_inline_const can only process anonymous constants"
|
||||
),
|
||||
};
|
||||
let expr = &tcx.hir().body(body_id).value;
|
||||
let ty = tcx.typeck(def_id).node_type(hir_id);
|
||||
|
||||
let lit_input = match expr.kind {
|
||||
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
|
||||
hir::ExprKind::Lit(ref lit) => {
|
||||
Some(LitToConstInput { lit: &lit.node, ty, neg: true })
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(lit_input) = lit_input {
|
||||
// If an error occurred, ignore that it's a literal and leave reporting the error up to
|
||||
// mir.
|
||||
match tcx.at(expr.span).lit_to_mir_constant(lit_input) {
|
||||
Ok(c) => return c,
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
|
||||
let parent_substs =
|
||||
tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
|
||||
let substs =
|
||||
ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
|
||||
.substs;
|
||||
|
||||
let uneval = UnevaluatedConst { def: def_id.to_def_id(), substs, promoted: None };
|
||||
debug_assert!(!uneval.has_free_regions());
|
||||
|
||||
Self::Unevaluated(uneval, ty)
|
||||
}
|
||||
|
||||
/// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
|
||||
/// converted to a constant, everything else becomes `Unevaluated`.
|
||||
#[instrument(skip(tcx), level = "debug", ret)]
|
||||
|
@ -1081,14 +1081,6 @@
|
||||
desc { "destructuring MIR constant"}
|
||||
}
|
||||
|
||||
/// Dereference a constant reference or raw pointer and turn the result into a constant
|
||||
/// again.
|
||||
query deref_mir_constant(
|
||||
key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
|
||||
) -> mir::ConstantKind<'tcx> {
|
||||
desc { "dereferencing MIR constant" }
|
||||
}
|
||||
|
||||
query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
|
||||
desc { "getting a &core::panic::Location referring to a span" }
|
||||
}
|
||||
@ -1100,10 +1092,6 @@
|
||||
desc { "converting literal to const" }
|
||||
}
|
||||
|
||||
query lit_to_mir_constant(key: LitToConstInput<'tcx>) -> Result<mir::ConstantKind<'tcx>, LitToConstError> {
|
||||
desc { "converting literal to mir constant" }
|
||||
}
|
||||
|
||||
query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
|
||||
desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { true }
|
||||
|
@ -106,7 +106,7 @@ pub fn as_constant_inner<'tcx>(
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, lit_input))]
|
||||
pub(crate) fn lit_to_mir_constant<'tcx>(
|
||||
fn lit_to_mir_constant<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
lit_input: LitToConstInput<'tcx>,
|
||||
) -> Result<ConstantKind<'tcx>, LitToConstError> {
|
||||
|
@ -1,4 +1,3 @@
|
||||
pub(crate) use crate::build::expr::as_constant::lit_to_mir_constant;
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::scope::DropKind;
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
|
@ -32,7 +32,6 @@
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.check_match = thir::pattern::check_match;
|
||||
providers.lit_to_const = thir::constant::lit_to_const;
|
||||
providers.lit_to_mir_constant = build::lit_to_mir_constant;
|
||||
providers.mir_built = build::mir_built;
|
||||
providers.thir_check_unsafety = check_unsafety::thir_check_unsafety;
|
||||
providers.thir_body = thir::cx::thir_body;
|
||||
|
@ -3,6 +3,8 @@
|
||||
use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
use crate::build::parse_float_into_scalar;
|
||||
|
||||
pub(crate) fn lit_to_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
lit_input: LitToConstInput<'tcx>,
|
||||
@ -46,12 +48,28 @@ pub(crate) fn lit_to_const<'tcx>(
|
||||
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
|
||||
ty::ValTree::from_scalar_int((*n).into())
|
||||
}
|
||||
(ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
|
||||
{
|
||||
let bytes = data as &[u8];
|
||||
ty::ValTree::from_raw_bytes(tcx, bytes)
|
||||
}
|
||||
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
|
||||
let scalar_int =
|
||||
trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?;
|
||||
ty::ValTree::from_scalar_int(scalar_int)
|
||||
}
|
||||
(ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
|
||||
(ast::LitKind::Float(n, _), ty::Float(fty)) => {
|
||||
let bits = parse_float_into_scalar(*n, *fty, neg)
|
||||
.ok_or_else(|| {
|
||||
LitToConstError::Reported(tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
format!("couldn't parse float literal: {:?}", lit_input.lit),
|
||||
))
|
||||
})?
|
||||
.assert_int();
|
||||
ty::ValTree::from_scalar_int(bits)
|
||||
}
|
||||
(ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
|
||||
(ast::LitKind::Err, _) => {
|
||||
return Err(LitToConstError::Reported(
|
||||
|
@ -1,13 +1,14 @@
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::Idx;
|
||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||
|
||||
@ -29,11 +30,11 @@ pub(super) fn const_to_pat(
|
||||
cv: mir::ConstantKind<'tcx>,
|
||||
id: hir::HirId,
|
||||
span: Span,
|
||||
mir_structural_match_violation: bool,
|
||||
check_body_for_struct_match_violation: Option<DefId>,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
let infcx = self.tcx.infer_ctxt().build();
|
||||
let mut convert = ConstToPat::new(self, id, span, infcx);
|
||||
convert.to_pat(cv, mir_structural_match_violation)
|
||||
convert.to_pat(cv, check_body_for_struct_match_violation)
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +105,7 @@ fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
|
||||
fn to_pat(
|
||||
&mut self,
|
||||
cv: mir::ConstantKind<'tcx>,
|
||||
mir_structural_match_violation: bool,
|
||||
check_body_for_struct_match_violation: Option<DefId>,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
trace!(self.treat_byte_string_as_slice);
|
||||
// This method is just a wrapper handling a validity check; the heavy lifting is
|
||||
@ -114,14 +115,44 @@ fn to_pat(
|
||||
// once indirect_structural_match is a full fledged error, this
|
||||
// level of indirection can be eliminated
|
||||
|
||||
let inlined_const_as_pat =
|
||||
self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| {
|
||||
Box::new(Pat {
|
||||
span: self.span,
|
||||
ty: cv.ty(),
|
||||
kind: PatKind::Constant { value: cv },
|
||||
})
|
||||
});
|
||||
let mir_structural_match_violation = check_body_for_struct_match_violation.map(|def_id| {
|
||||
// `mir_const_qualif` must be called with the `DefId` of the item where the const is
|
||||
// defined, not where it is declared. The difference is significant for associated
|
||||
// constants.
|
||||
self.tcx().mir_const_qualif(def_id).custom_eq
|
||||
});
|
||||
debug!(?check_body_for_struct_match_violation, ?mir_structural_match_violation);
|
||||
|
||||
let inlined_const_as_pat = match cv {
|
||||
mir::ConstantKind::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(valtree) => self
|
||||
.recur(valtree, cv.ty(), mir_structural_match_violation.unwrap_or(false))
|
||||
.unwrap_or_else(|_| {
|
||||
Box::new(Pat {
|
||||
span: self.span,
|
||||
ty: cv.ty(),
|
||||
kind: PatKind::Constant { value: cv },
|
||||
})
|
||||
}),
|
||||
},
|
||||
mir::ConstantKind::Unevaluated(_, _) => {
|
||||
span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}")
|
||||
}
|
||||
mir::ConstantKind::Val(_, _) => Box::new(Pat {
|
||||
span: self.span,
|
||||
ty: cv.ty(),
|
||||
kind: PatKind::Constant { value: cv },
|
||||
}),
|
||||
};
|
||||
|
||||
if !self.saw_const_match_error.get() {
|
||||
// If we were able to successfully convert the const to some pat,
|
||||
@ -141,29 +172,70 @@ fn to_pat(
|
||||
//
|
||||
// FIXME(#73448): Find a way to bring const qualification into parity with
|
||||
// `search_for_structural_match_violation`.
|
||||
if structural.is_none() && mir_structural_match_violation {
|
||||
if structural.is_none() && mir_structural_match_violation.unwrap_or(false) {
|
||||
warn!("MIR const-checker found novel structural match violation. See #73448.");
|
||||
return inlined_const_as_pat;
|
||||
}
|
||||
|
||||
if let Some(non_sm_ty) = structural {
|
||||
if !self.type_may_have_partial_eq_impl(cv.ty()) {
|
||||
// fatal avoids ICE from resolution of nonexistent method (rare case).
|
||||
self.tcx()
|
||||
.sess
|
||||
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
|
||||
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
|
||||
self.tcx().emit_spanned_lint(
|
||||
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
|
||||
self.id,
|
||||
self.span,
|
||||
IndirectStructuralMatch { non_sm_ty },
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"`search_for_structural_match_violation` found one, but `CustomEq` was \
|
||||
not in the qualifs for that `const`"
|
||||
);
|
||||
if let ty::Adt(def, ..) = non_sm_ty.kind() {
|
||||
if def.is_union() {
|
||||
let err = UnionPattern { span: self.span };
|
||||
self.tcx().sess.emit_err(err);
|
||||
} else {
|
||||
// fatal avoids ICE from resolution of nonexistent method (rare case).
|
||||
self.tcx()
|
||||
.sess
|
||||
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty });
|
||||
}
|
||||
} else {
|
||||
let err = InvalidPattern { span: self.span, non_sm_ty };
|
||||
self.tcx().sess.emit_err(err);
|
||||
return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild });
|
||||
}
|
||||
} else if !self.saw_const_match_lint.get() {
|
||||
if let Some(mir_structural_match_violation) = mir_structural_match_violation {
|
||||
match non_sm_ty.kind() {
|
||||
ty::RawPtr(pointee)
|
||||
if pointee.ty.is_sized(self.tcx(), self.param_env) => {}
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => {
|
||||
self.tcx().emit_spanned_lint(
|
||||
lint::builtin::POINTER_STRUCTURAL_MATCH,
|
||||
self.id,
|
||||
self.span,
|
||||
PointerPattern,
|
||||
);
|
||||
}
|
||||
ty::Adt(..) if mir_structural_match_violation => {
|
||||
self.tcx().emit_spanned_lint(
|
||||
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
|
||||
self.id,
|
||||
self.span,
|
||||
IndirectStructuralMatch { non_sm_ty },
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
debug!(
|
||||
"`search_for_structural_match_violation` found one, but `CustomEq` was \
|
||||
not in the qualifs for that `const`"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !self.saw_const_match_lint.get() {
|
||||
match cv.ty().kind() {
|
||||
ty::RawPtr(pointee) if pointee.ty.is_sized(self.tcx(), self.param_env) => {}
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => {
|
||||
self.tcx().emit_spanned_lint(
|
||||
lint::builtin::POINTER_STRUCTURAL_MATCH,
|
||||
self.id,
|
||||
self.span,
|
||||
PointerPattern,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,6 +243,7 @@ fn to_pat(
|
||||
inlined_const_as_pat
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
|
||||
// double-check there even *is* a semantic `PartialEq` to dispatch to.
|
||||
//
|
||||
@ -187,29 +260,19 @@ fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
|
||||
);
|
||||
|
||||
// FIXME: should this call a `predicate_must_hold` variant instead?
|
||||
let has_impl = self.infcx.predicate_may_hold(&partial_eq_obligation);
|
||||
|
||||
// Note: To fix rust-lang/rust#65466, we could just remove this type
|
||||
// walk hack for function pointers, and unconditionally error
|
||||
// if `PartialEq` is not implemented. However, that breaks stable
|
||||
// code at the moment, because types like `for <'a> fn(&'a ())` do
|
||||
// not *yet* implement `PartialEq`. So for now we leave this here.
|
||||
has_impl
|
||||
|| ty.walk().any(|t| match t.unpack() {
|
||||
ty::subst::GenericArgKind::Lifetime(_) => false,
|
||||
ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
|
||||
ty::subst::GenericArgKind::Const(_) => false,
|
||||
})
|
||||
self.infcx.predicate_may_hold(&partial_eq_obligation)
|
||||
}
|
||||
|
||||
fn field_pats(
|
||||
&self,
|
||||
vals: impl Iterator<Item = mir::ConstantKind<'tcx>>,
|
||||
vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
|
||||
) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
|
||||
vals.enumerate()
|
||||
.map(|(idx, val)| {
|
||||
.map(|(idx, (val, ty))| {
|
||||
let field = FieldIdx::new(idx);
|
||||
Ok(FieldPat { field, pattern: self.recur(val, false)? })
|
||||
// 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, false)? })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -218,7 +281,8 @@ fn field_pats(
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn recur(
|
||||
&self,
|
||||
cv: mir::ConstantKind<'tcx>,
|
||||
cv: ValTree<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
mir_structural_match_violation: bool,
|
||||
) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> {
|
||||
let id = self.id;
|
||||
@ -226,8 +290,9 @@ fn recur(
|
||||
let tcx = self.tcx();
|
||||
let param_env = self.param_env;
|
||||
|
||||
let kind = match cv.ty().kind() {
|
||||
let kind = match ty.kind() {
|
||||
ty::Float(_) => {
|
||||
self.saw_const_match_lint.set(true);
|
||||
tcx.emit_spanned_lint(
|
||||
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
|
||||
id,
|
||||
@ -236,27 +301,6 @@ fn recur(
|
||||
);
|
||||
return Err(FallbackToConstRef);
|
||||
}
|
||||
ty::Adt(adt_def, _) if adt_def.is_union() => {
|
||||
// Matching on union fields is unsafe, we can't hide it in constants
|
||||
self.saw_const_match_error.set(true);
|
||||
let err = UnionPattern { span };
|
||||
tcx.sess.emit_err(err);
|
||||
PatKind::Wild
|
||||
}
|
||||
ty::Adt(..)
|
||||
if !self.type_may_have_partial_eq_impl(cv.ty())
|
||||
// FIXME(#73448): Find a way to bring const qualification into parity with
|
||||
// `search_for_structural_match_violation` and then remove this condition.
|
||||
|
||||
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
|
||||
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
|
||||
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
|
||||
{
|
||||
self.saw_const_match_error.set(true);
|
||||
let err = TypeNotStructural { span, non_sm_ty };
|
||||
tcx.sess.emit_err(err);
|
||||
PatKind::Wild
|
||||
}
|
||||
// If the type is not structurally comparable, just emit the constant directly,
|
||||
// causing the pattern match code to treat it opaquely.
|
||||
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
|
||||
@ -266,16 +310,14 @@ fn recur(
|
||||
// details.
|
||||
// Backwards compatibility hack because we can't cause hard errors on these
|
||||
// types, so we compare them via `PartialEq::eq` at runtime.
|
||||
ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
|
||||
if !self.saw_const_match_error.get()
|
||||
&& !self.saw_const_match_lint.get()
|
||||
{
|
||||
ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => {
|
||||
if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
|
||||
self.saw_const_match_lint.set(true);
|
||||
tcx.emit_spanned_lint(
|
||||
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
|
||||
id,
|
||||
span,
|
||||
IndirectStructuralMatch { non_sm_ty: cv.ty() },
|
||||
IndirectStructuralMatch { non_sm_ty: ty },
|
||||
);
|
||||
}
|
||||
// Since we are behind a reference, we can just bubble the error up so we get a
|
||||
@ -283,77 +325,75 @@ fn recur(
|
||||
// `PartialEq::eq` on it.
|
||||
return Err(FallbackToConstRef);
|
||||
}
|
||||
ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
|
||||
debug!(
|
||||
"adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
|
||||
adt_def,
|
||||
cv.ty()
|
||||
);
|
||||
ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
|
||||
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,);
|
||||
self.saw_const_match_error.set(true);
|
||||
let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
|
||||
let err = TypeNotStructural { span, non_sm_ty: ty };
|
||||
tcx.sess.emit_err(err);
|
||||
PatKind::Wild
|
||||
}
|
||||
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
|
||||
let destructured = tcx.destructure_mir_constant(param_env, cv);
|
||||
|
||||
let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
|
||||
let variant_index =
|
||||
VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
|
||||
PatKind::Variant {
|
||||
adt_def: *adt_def,
|
||||
substs,
|
||||
variant_index: destructured
|
||||
.variant
|
||||
.expect("destructed const of adt without variant id"),
|
||||
subpatterns: self.field_pats(destructured.fields.iter().copied())?,
|
||||
variant_index,
|
||||
subpatterns: self.field_pats(
|
||||
fields.iter().copied().zip(
|
||||
adt_def.variants()[variant_index]
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx(), substs)),
|
||||
),
|
||||
)?,
|
||||
}
|
||||
}
|
||||
ty::Tuple(_) | ty::Adt(_, _) => {
|
||||
let destructured = tcx.destructure_mir_constant(param_env, cv);
|
||||
PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? }
|
||||
}
|
||||
ty::Array(..) => PatKind::Array {
|
||||
prefix: tcx
|
||||
.destructure_mir_constant(param_env, cv)
|
||||
.fields
|
||||
ty::Tuple(fields) => PatKind::Leaf {
|
||||
subpatterns: self
|
||||
.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?,
|
||||
},
|
||||
ty::Adt(def, substs) => PatKind::Leaf {
|
||||
subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
|
||||
def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), substs)),
|
||||
))?,
|
||||
},
|
||||
ty::Array(elem_ty, _) => PatKind::Array {
|
||||
prefix: cv
|
||||
.unwrap_branch()
|
||||
.iter()
|
||||
.map(|val| self.recur(*val, false))
|
||||
.map(|val| self.recur(*val, *elem_ty, false))
|
||||
.collect::<Result<_, _>>()?,
|
||||
slice: None,
|
||||
suffix: Box::new([]),
|
||||
},
|
||||
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
|
||||
// These are not allowed and will error elsewhere anyway.
|
||||
ty::Dynamic(..) => {
|
||||
self.saw_const_match_error.set(true);
|
||||
let err = InvalidPattern { span, non_sm_ty: cv.ty() };
|
||||
tcx.sess.emit_err(err);
|
||||
PatKind::Wild
|
||||
}
|
||||
// `&str` is represented as `ConstValue::Slice`, let's keep using this
|
||||
// `&str` is represented as a valtree, let's keep using this
|
||||
// optimization for now.
|
||||
ty::Str => PatKind::Constant { value: cv },
|
||||
ty::Str => PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) },
|
||||
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
|
||||
// matching against references, you can only use byte string literals.
|
||||
// The typechecker has a special case for byte string literals, by treating them
|
||||
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
|
||||
// has no negative effects on pattern matching, even if we're actually matching on
|
||||
// arrays.
|
||||
ty::Array(..) if !self.treat_byte_string_as_slice => {
|
||||
ty::Array(elem_ty, _) if !self.treat_byte_string_as_slice => {
|
||||
let old = self.behind_reference.replace(true);
|
||||
let array = tcx.deref_mir_constant(self.param_env.and(cv));
|
||||
// References have the same valtree representation as their pointee.
|
||||
let array = cv;
|
||||
let val = PatKind::Deref {
|
||||
subpattern: Box::new(Pat {
|
||||
kind: PatKind::Array {
|
||||
prefix: tcx
|
||||
.destructure_mir_constant(param_env, array)
|
||||
.fields
|
||||
prefix: array.unwrap_branch()
|
||||
.iter()
|
||||
.map(|val| self.recur(*val, false))
|
||||
.map(|val| self.recur(*val, elem_ty, false))
|
||||
.collect::<Result<_, _>>()?,
|
||||
slice: None,
|
||||
suffix: Box::new([]),
|
||||
},
|
||||
span,
|
||||
ty: *pointee_ty,
|
||||
ty: tcx.mk_slice(elem_ty),
|
||||
}),
|
||||
};
|
||||
self.behind_reference.set(old);
|
||||
@ -365,15 +405,14 @@ fn recur(
|
||||
// pattern.
|
||||
ty::Slice(elem_ty) => {
|
||||
let old = self.behind_reference.replace(true);
|
||||
let array = tcx.deref_mir_constant(self.param_env.and(cv));
|
||||
// References have the same valtree representation as their pointee.
|
||||
let array = cv;
|
||||
let val = PatKind::Deref {
|
||||
subpattern: Box::new(Pat {
|
||||
kind: PatKind::Slice {
|
||||
prefix: tcx
|
||||
.destructure_mir_constant(param_env, array)
|
||||
.fields
|
||||
prefix: array.unwrap_branch()
|
||||
.iter()
|
||||
.map(|val| self.recur(*val, false))
|
||||
.map(|val| self.recur(*val, elem_ty, false))
|
||||
.collect::<Result<_, _>>()?,
|
||||
slice: None,
|
||||
suffix: Box::new([]),
|
||||
@ -418,48 +457,28 @@ fn recur(
|
||||
// deref pattern.
|
||||
_ => {
|
||||
if !pointee_ty.is_sized(tcx, param_env) {
|
||||
// `tcx.deref_mir_constant()` below will ICE with an unsized type
|
||||
// (except slices, which are handled in a separate arm above).
|
||||
|
||||
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
|
||||
tcx.sess.emit_err(err);
|
||||
|
||||
// FIXME: introduce PatKind::Error to silence follow up diagnostics due to unreachable patterns.
|
||||
PatKind::Wild
|
||||
} else {
|
||||
let old = self.behind_reference.replace(true);
|
||||
let subpattern = self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false)?;
|
||||
// References have the same valtree representation as their pointee.
|
||||
let subpattern = self.recur(cv, *pointee_ty, false)?;
|
||||
self.behind_reference.set(old);
|
||||
PatKind::Deref { subpattern }
|
||||
}
|
||||
}
|
||||
},
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
|
||||
PatKind::Constant { value: cv }
|
||||
}
|
||||
ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => {
|
||||
return Err(FallbackToConstRef);
|
||||
}
|
||||
// FIXME: these can have very surprising behaviour where optimization levels or other
|
||||
// compilation choices change the runtime behaviour of the match.
|
||||
// See https://github.com/rust-lang/rust/issues/70861 for examples.
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => {
|
||||
if !self.saw_const_match_error.get()
|
||||
&& !self.saw_const_match_lint.get()
|
||||
{
|
||||
self.saw_const_match_lint.set(true);
|
||||
tcx.emit_spanned_lint(
|
||||
lint::builtin::POINTER_STRUCTURAL_MATCH,
|
||||
id,
|
||||
span,
|
||||
PointerPattern
|
||||
);
|
||||
}
|
||||
return Err(FallbackToConstRef);
|
||||
PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) }
|
||||
}
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(),
|
||||
_ => {
|
||||
self.saw_const_match_error.set(true);
|
||||
let err = InvalidPattern { span, non_sm_ty: cv.ty() };
|
||||
tcx.sess.emit_err(err);
|
||||
let err = InvalidPattern { span, non_sm_ty: ty };
|
||||
tcx.sess.emit_err(err);
|
||||
PatKind::Wild
|
||||
}
|
||||
};
|
||||
@ -472,7 +491,7 @@ fn recur(
|
||||
|
||||
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
|
||||
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
|
||||
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
|
||||
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, ty)
|
||||
{
|
||||
self.saw_const_match_lint.set(true);
|
||||
tcx.emit_spanned_lint(
|
||||
@ -483,6 +502,6 @@ fn recur(
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Box::new(Pat { span, ty: cv.ty(), kind }))
|
||||
Ok(Box::new(Pat { span, ty, kind }))
|
||||
}
|
||||
}
|
||||
|
@ -53,11 +53,11 @@
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_hir::{HirId, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
|
||||
use rustc_middle::{middle::stability::EvalResult, mir::interpret::ConstValue};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT};
|
||||
@ -140,28 +140,17 @@ fn from_constant<'tcx>(
|
||||
value: mir::ConstantKind<'tcx>,
|
||||
) -> Option<IntRange> {
|
||||
let ty = value.ty();
|
||||
if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) {
|
||||
let val = if let mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) = value {
|
||||
// For this specific pattern we can skip a lot of effort and go
|
||||
// straight to the result, after doing a bit of checking. (We
|
||||
// could remove this branch and just fall through, which
|
||||
// is more general but much slower.)
|
||||
scalar.to_bits_or_ptr_internal(target_size).unwrap().left()?
|
||||
} else {
|
||||
if let mir::ConstantKind::Ty(c) = value
|
||||
&& let ty::ConstKind::Value(_) = c.kind()
|
||||
{
|
||||
bug!("encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val");
|
||||
}
|
||||
let (target_size, bias) = Self::integral_size_and_signed_bias(tcx, ty)?;
|
||||
let val = match value {
|
||||
mir::ConstantKind::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => {
|
||||
valtree.unwrap_leaf().to_bits(target_size).ok()
|
||||
},
|
||||
// This is a more general form of the previous case.
|
||||
_ => value.try_eval_bits(tcx, param_env, ty),
|
||||
}?;
|
||||
|
||||
// This is a more general form of the previous case.
|
||||
value.try_eval_bits(tcx, param_env, ty)?
|
||||
};
|
||||
let val = val ^ bias;
|
||||
Some(IntRange { range: val..=val, bias })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let val = val ^ bias;
|
||||
Some(IntRange { range: val..=val, bias })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -18,14 +18,15 @@
|
||||
use rustc_hir::RangeEnd;
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::mir::interpret::{
|
||||
ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar,
|
||||
ConstValue, ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
|
||||
};
|
||||
use rustc_middle::mir::{self, UserTypeProjection};
|
||||
use rustc_middle::mir::{self, ConstantKind, UserTypeProjection};
|
||||
use rustc_middle::mir::{BorrowKind, Mutability};
|
||||
use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange};
|
||||
use rustc_middle::ty::subst::{GenericArg, SubstsRef};
|
||||
use rustc_middle::ty::CanonicalUserTypeAnnotation;
|
||||
use rustc_middle::ty::{self, AdtDef, ConstKind, Region, Ty, TyCtxt, UserType};
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, AdtDef, Region, Ty, TyCtxt, UserType};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
|
||||
@ -518,16 +519,24 @@ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) ->
|
||||
}
|
||||
};
|
||||
|
||||
// `mir_const_qualif` must be called with the `DefId` of the item where the const is
|
||||
// defined, not where it is declared. The difference is significant for associated
|
||||
// constants.
|
||||
let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq;
|
||||
debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation);
|
||||
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, Some(span))
|
||||
.map(|val| match val {
|
||||
Some(valtree) => mir::ConstantKind::Ty(self.tcx.mk_const(valtree, ty)),
|
||||
None => mir::ConstantKind::Val(
|
||||
self.tcx
|
||||
.const_eval_global_id(param_env_reveal_all, cid, Some(span))
|
||||
.expect("const_eval_global_id_for_typeck should have already failed"),
|
||||
ty,
|
||||
),
|
||||
});
|
||||
|
||||
match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) {
|
||||
Ok(literal) => {
|
||||
let const_ = mir::ConstantKind::Val(literal, ty);
|
||||
let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation);
|
||||
match const_value {
|
||||
Ok(const_) => {
|
||||
let pattern = self.const_to_pat(const_, id, span, Some(instance.def_id()));
|
||||
|
||||
if !is_associated_const {
|
||||
return pattern;
|
||||
@ -577,27 +586,69 @@ fn lower_inline_const(
|
||||
id: hir::HirId,
|
||||
span: Span,
|
||||
) -> PatKind<'tcx> {
|
||||
let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const.def_id);
|
||||
let tcx = self.tcx;
|
||||
let def_id = anon_const.def_id;
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let body_id = match tcx.hir().get(hir_id) {
|
||||
hir::Node::AnonConst(ac) => ac.body,
|
||||
_ => span_bug!(
|
||||
tcx.def_span(def_id.to_def_id()),
|
||||
"from_inline_const can only process anonymous constants"
|
||||
),
|
||||
};
|
||||
let expr = &tcx.hir().body(body_id).value;
|
||||
let ty = tcx.typeck(def_id).node_type(hir_id);
|
||||
|
||||
// Evaluate early like we do in `lower_path`.
|
||||
let value = value.eval(self.tcx, self.param_env);
|
||||
|
||||
match value {
|
||||
mir::ConstantKind::Ty(c) => match c.kind() {
|
||||
ConstKind::Param(_) => {
|
||||
self.tcx.sess.emit_err(ConstParamInPattern { span });
|
||||
return PatKind::Wild;
|
||||
// Special case inline consts that are just literals. This is solely
|
||||
// a performance optimization, as we could also just go through the regular
|
||||
// const eval path below.
|
||||
// FIXME: investigate the performance impact of removing this.
|
||||
let lit_input = match expr.kind {
|
||||
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
|
||||
hir::ExprKind::Lit(ref lit) => {
|
||||
Some(LitToConstInput { lit: &lit.node, ty, neg: true })
|
||||
}
|
||||
ConstKind::Error(_) => {
|
||||
return PatKind::Wild;
|
||||
}
|
||||
_ => bug!("Expected ConstKind::Param"),
|
||||
_ => None,
|
||||
},
|
||||
mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind,
|
||||
mir::ConstantKind::Unevaluated(..) => {
|
||||
// If we land here it means the const can't be evaluated because it's `TooGeneric`.
|
||||
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
|
||||
return PatKind::Wild;
|
||||
_ => None,
|
||||
};
|
||||
if let Some(lit_input) = lit_input {
|
||||
match tcx.at(expr.span).lit_to_const(lit_input) {
|
||||
Ok(c) => return self.const_to_pat(ConstantKind::Ty(c), id, span, None).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.
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
|
||||
let parent_substs =
|
||||
tcx.erase_regions(ty::InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
|
||||
let substs =
|
||||
ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
|
||||
.substs;
|
||||
|
||||
let uneval = mir::UnevaluatedConst { def: def_id.to_def_id(), substs, promoted: None };
|
||||
debug_assert!(!substs.has_free_regions());
|
||||
|
||||
let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), substs: substs };
|
||||
// First try using a valtree in order to destructure the constant into a pattern.
|
||||
if let Ok(Some(valtree)) =
|
||||
self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
|
||||
{
|
||||
self.const_to_pat(ConstantKind::Ty(self.tcx.mk_const(valtree, ty)), id, span, None).kind
|
||||
} else {
|
||||
// If that fails, convert it to an opaque constant pattern.
|
||||
match tcx.const_eval_resolve(self.param_env, uneval, None) {
|
||||
Ok(val) => self.const_to_pat(mir::ConstantKind::Val(val, ty), id, span, None).kind,
|
||||
Err(ErrorHandled::TooGeneric) => {
|
||||
// If we land here it means the const can't be evaluated because it's `TooGeneric`.
|
||||
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
|
||||
PatKind::Wild
|
||||
}
|
||||
Err(ErrorHandled::Reported(_)) => PatKind::Wild,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -626,8 +677,10 @@ fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> {
|
||||
|
||||
let lit_input =
|
||||
LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
|
||||
match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) {
|
||||
Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind,
|
||||
match self.tcx.at(expr.span).lit_to_const(lit_input) {
|
||||
Ok(constant) => {
|
||||
self.const_to_pat(ConstantKind::Ty(constant), expr.hir_id, lit.span, None).kind
|
||||
}
|
||||
Err(LitToConstError::Reported(_)) => PatKind::Wild,
|
||||
Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
|
||||
}
|
||||
@ -806,6 +859,9 @@ pub(crate) fn compare_const_vals<'tcx>(
|
||||
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(a)), _a_ty),
|
||||
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty),
|
||||
) => return Some(a.cmp(&b)),
|
||||
(mir::ConstantKind::Ty(a), mir::ConstantKind::Ty(b)) => {
|
||||
return Some(a.kind().cmp(&b.kind()));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
|
||||
let literal = constant.literal;
|
||||
match literal {
|
||||
ConstantKind::Ty(c) => match c.kind() {
|
||||
ConstKind::Param(_) | ConstKind::Error(_) => {}
|
||||
_ => bug!("only ConstKind::Param should be encountered here, got {:#?}", c),
|
||||
ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {}
|
||||
_ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
|
||||
},
|
||||
ConstantKind::Unevaluated(..) => self.required_consts.push(*constant),
|
||||
ConstantKind::Val(..) => {}
|
||||
|
@ -1288,7 +1288,7 @@ pub fn decode_expn_id(
|
||||
decode_data: impl FnOnce(ExpnId) -> (ExpnData, ExpnHash),
|
||||
) -> ExpnId {
|
||||
if index == 0 {
|
||||
debug!("decode_expn_id: deserialized root");
|
||||
trace!("decode_expn_id: deserialized root");
|
||||
return ExpnId::root();
|
||||
}
|
||||
|
||||
@ -1321,7 +1321,7 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
|
||||
) -> SyntaxContext {
|
||||
let raw_id: u32 = Decodable::decode(d);
|
||||
if raw_id == 0 {
|
||||
debug!("decode_syntax_context: deserialized root");
|
||||
trace!("decode_syntax_context: deserialized root");
|
||||
// The root is special
|
||||
return SyntaxContext::root();
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#![feature(inline_const)]
|
||||
|
||||
// Test that we don't generate a memory allocation for the constant
|
||||
// and read the fields from that, but instead just create the value pair directly.
|
||||
pub fn foo() -> (i32, i32) {
|
||||
// CHECK: ret { i32, i32 } { i32 1, i32 2 }
|
||||
const { (1, 2) }
|
||||
|
@ -1,7 +1,4 @@
|
||||
// revisions: cfail1
|
||||
// should-ice
|
||||
// error-pattern: forcing query
|
||||
// known-bug: #101518
|
||||
// revisions: cpass
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct Id<'a> {
|
||||
@ -9,9 +6,7 @@ struct Id<'a> {
|
||||
}
|
||||
fn visit_struct() {
|
||||
let id = Id { ns: "random1" };
|
||||
const FLAG: Id<'static> = Id {
|
||||
ns: "needs_to_be_the_same",
|
||||
};
|
||||
const FLAG: Id<'static> = Id { ns: "needs_to_be_the_same" };
|
||||
match id {
|
||||
FLAG => {}
|
||||
_ => {}
|
||||
@ -19,9 +14,7 @@ fn visit_struct() {
|
||||
}
|
||||
fn visit_struct2() {
|
||||
let id = Id { ns: "random2" };
|
||||
const FLAG: Id<'static> = Id {
|
||||
ns: "needs_to_be_the_same",
|
||||
};
|
||||
const FLAG: Id<'static> = Id { ns: "needs_to_be_the_same" };
|
||||
match id {
|
||||
FLAG => {}
|
||||
_ => {}
|
||||
|
@ -35,7 +35,7 @@ fn foo(_1: Option<String>) -> i32 {
|
||||
// + literal: Const { ty: for<'a, 'b> fn(&'a str, &'b str) -> bool {<str as PartialEq>::eq}, val: Value(<ZST>) }
|
||||
// mir::Constant
|
||||
// + span: $DIR/string.rs:9:14: 9:17
|
||||
// + literal: Const { ty: &str, val: Value(Slice(..)) }
|
||||
// + literal: Const { ty: &str, val: Value(ValTree::Branch(..)) }
|
||||
}
|
||||
|
||||
bb3: {
|
||||
|
@ -4,7 +4,7 @@ fn main() {
|
||||
let a: &dyn Send = &7u32;
|
||||
match a {
|
||||
F => panic!(),
|
||||
//~^ ERROR `&dyn Send` cannot be used in patterns
|
||||
//~^ ERROR `dyn Send` cannot be used in patterns
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: `&dyn Send` cannot be used in patterns
|
||||
error: `dyn Send` cannot be used in patterns
|
||||
--> $DIR/issue-70972-dyn-trait.rs:6:9
|
||||
|
|
||||
LL | F => panic!(),
|
||||
|
@ -3,6 +3,6 @@
|
||||
fn main() {
|
||||
let a: &dyn PartialEq<u32> = &7u32;
|
||||
match a {
|
||||
F => panic!(), //~ ERROR: `&dyn PartialEq<u32>` cannot be used in patterns
|
||||
F => panic!(), //~ ERROR: `dyn PartialEq<u32>` cannot be used in patterns
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: `&dyn PartialEq<u32>` cannot be used in patterns
|
||||
error: `dyn PartialEq<u32>` cannot be used in patterns
|
||||
--> $DIR/issue-72565.rs:6:9
|
||||
|
|
||||
LL | F => panic!(),
|
||||
|
Loading…
Reference in New Issue
Block a user