Only rewrite valtree-constants to patterns and keep other constants opaque

This commit is contained in:
Oli Scherer 2023-02-14 09:17:34 +00:00
parent 7a2f47c271
commit d030ece6f7
11 changed files with 235 additions and 174 deletions

View file

@ -2487,8 +2487,8 @@ impl<'tcx> ConstantKind<'tcx> {
if let Some(lit_input) = lit_input { if let Some(lit_input) = lit_input {
// If an error occurred, ignore that it's a literal and leave reporting the error up to // If an error occurred, ignore that it's a literal and leave reporting the error up to
// mir. // mir.
match tcx.at(expr.span).lit_to_mir_constant(lit_input) { match tcx.at(expr.span).lit_to_const(lit_input) {
Ok(c) => return c, Ok(c) => return Self::Ty(c),
Err(_) => {} Err(_) => {}
} }
} }

View file

@ -1,13 +1,14 @@
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::thir::{FieldPat, Pat, PatKind}; 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_session::lint;
use rustc_span::Span; 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::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCause}; use rustc_trait_selection::traits::{self, ObligationCause};
@ -29,11 +30,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
cv: mir::ConstantKind<'tcx>, cv: mir::ConstantKind<'tcx>,
id: hir::HirId, id: hir::HirId,
span: Span, span: Span,
mir_structural_match_violation: bool, check_body_for_struct_match_violation: Option<DefId>,
) -> Box<Pat<'tcx>> { ) -> Box<Pat<'tcx>> {
let infcx = self.tcx.infer_ctxt().build(); let infcx = self.tcx.infer_ctxt().build();
let mut convert = ConstToPat::new(self, id, span, infcx); 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 @@ impl<'tcx> ConstToPat<'tcx> {
fn to_pat( fn to_pat(
&mut self, &mut self,
cv: mir::ConstantKind<'tcx>, cv: mir::ConstantKind<'tcx>,
mir_structural_match_violation: bool, check_body_for_struct_match_violation: Option<DefId>,
) -> Box<Pat<'tcx>> { ) -> Box<Pat<'tcx>> {
trace!(self.treat_byte_string_as_slice); trace!(self.treat_byte_string_as_slice);
// This method is just a wrapper handling a validity check; the heavy lifting is // This method is just a wrapper handling a validity check; the heavy lifting is
@ -114,14 +115,44 @@ impl<'tcx> ConstToPat<'tcx> {
// once indirect_structural_match is a full fledged error, this // once indirect_structural_match is a full fledged error, this
// level of indirection can be eliminated // level of indirection can be eliminated
let inlined_const_as_pat = let mir_structural_match_violation = check_body_for_struct_match_violation.map(|def_id| {
self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| { // `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, "unevaluated 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 { Box::new(Pat {
span: self.span, span: self.span,
ty: cv.ty(), ty: cv.ty(),
kind: PatKind::Constant { value: cv }, 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 !self.saw_const_match_error.get() {
// If we were able to successfully convert the const to some pat, // If we were able to successfully convert the const to some pat,
@ -141,25 +172,50 @@ impl<'tcx> ConstToPat<'tcx> {
// //
// FIXME(#73448): Find a way to bring const qualification into parity with // FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation`. // `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."); warn!("MIR const-checker found novel structural match violation. See #73448.");
return inlined_const_as_pat; return inlined_const_as_pat;
} }
if let Some(non_sm_ty) = structural { if let Some(non_sm_ty) = structural {
if !self.type_may_have_partial_eq_impl(cv.ty()) { if !self.type_may_have_partial_eq_impl(cv.ty()) {
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). // fatal avoids ICE from resolution of nonexistent method (rare case).
self.tcx() self.tcx()
.sess .sess
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty }); .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty });
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() { }
} 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( self.tcx().emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH, lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id, self.id,
self.span, self.span,
IndirectStructuralMatch { non_sm_ty }, IndirectStructuralMatch { non_sm_ty },
); );
} else { }
_ => {
debug!( debug!(
"`search_for_structural_match_violation` found one, but `CustomEq` was \ "`search_for_structural_match_violation` found one, but `CustomEq` was \
not in the qualifs for that `const`" not in the qualifs for that `const`"
@ -167,10 +223,27 @@ impl<'tcx> ConstToPat<'tcx> {
} }
} }
} }
}
} 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,
);
}
_ => {}
}
}
}
inlined_const_as_pat inlined_const_as_pat
} }
#[instrument(level = "trace", skip(self), ret)]
fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
// double-check there even *is* a semantic `PartialEq` to dispatch to. // double-check there even *is* a semantic `PartialEq` to dispatch to.
// //
@ -192,12 +265,14 @@ impl<'tcx> ConstToPat<'tcx> {
fn field_pats( fn field_pats(
&self, &self,
vals: impl Iterator<Item = mir::ConstantKind<'tcx>>, vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> { ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
vals.enumerate() vals.enumerate()
.map(|(idx, val)| { .map(|(idx, (val, ty))| {
let field = FieldIdx::new(idx); 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() .collect()
} }
@ -206,7 +281,8 @@ impl<'tcx> ConstToPat<'tcx> {
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
fn recur( fn recur(
&self, &self,
cv: mir::ConstantKind<'tcx>, cv: ValTree<'tcx>,
ty: Ty<'tcx>,
mir_structural_match_violation: bool, mir_structural_match_violation: bool,
) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> { ) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> {
let id = self.id; let id = self.id;
@ -214,8 +290,9 @@ impl<'tcx> ConstToPat<'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
let param_env = self.param_env; let param_env = self.param_env;
let kind = match cv.ty().kind() { let kind = match ty.kind() {
ty::Float(_) => { ty::Float(_) => {
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint( tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id, id,
@ -224,27 +301,6 @@ impl<'tcx> ConstToPat<'tcx> {
); );
return Err(FallbackToConstRef); 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, // If the type is not structurally comparable, just emit the constant directly,
// causing the pattern match code to treat it opaquely. // causing the pattern match code to treat it opaquely.
// FIXME: This code doesn't emit errors itself, the caller emits the errors. // FIXME: This code doesn't emit errors itself, the caller emits the errors.
@ -254,16 +310,14 @@ impl<'tcx> ConstToPat<'tcx> {
// details. // details.
// Backwards compatibility hack because we can't cause hard errors on these // Backwards compatibility hack because we can't cause hard errors on these
// types, so we compare them via `PartialEq::eq` at runtime. // types, so we compare them via `PartialEq::eq` at runtime.
ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => { ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => {
if !self.saw_const_match_error.get() if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true); self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint( tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH, lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id, id,
span, 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 // Since we are behind a reference, we can just bubble the error up so we get a
@ -271,39 +325,45 @@ impl<'tcx> ConstToPat<'tcx> {
// `PartialEq::eq` on it. // `PartialEq::eq` on it.
return Err(FallbackToConstRef); return Err(FallbackToConstRef);
} }
ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => { ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
debug!( debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,);
"adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
adt_def,
cv.ty()
);
self.saw_const_match_error.set(true); 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); tcx.sess.emit_err(err);
PatKind::Wild PatKind::Wild
} }
ty::Adt(adt_def, substs) if adt_def.is_enum() => { 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 { PatKind::Variant {
adt_def: *adt_def, adt_def: *adt_def,
substs, substs,
variant_index: destructured variant_index,
.variant subpatterns: self.field_pats(
.expect("destructed const of adt without variant id"), fields.iter().copied().zip(
subpatterns: self.field_pats(destructured.fields.iter().copied())?, adt_def.variants()[variant_index]
}
}
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 .fields
.iter() .iter()
.map(|val| self.recur(*val, false)) .map(|field| field.ty(self.tcx(), substs)),
),
)?,
}
}
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, *elem_ty, false))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
slice: None, slice: None,
suffix: Box::new([]), suffix: Box::new([]),
@ -312,36 +372,35 @@ impl<'tcx> ConstToPat<'tcx> {
// These are not allowed and will error elsewhere anyway. // These are not allowed and will error elsewhere anyway.
ty::Dynamic(..) => { ty::Dynamic(..) => {
self.saw_const_match_error.set(true); self.saw_const_match_error.set(true);
let err = InvalidPattern { span, non_sm_ty: cv.ty() }; let err = InvalidPattern { span, non_sm_ty: ty };
tcx.sess.emit_err(err); tcx.sess.emit_err(err);
PatKind::Wild 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. // 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 // `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. // matching against references, you can only use byte string literals.
// The typechecker has a special case for byte string literals, by treating them // 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 // 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 // has no negative effects on pattern matching, even if we're actually matching on
// arrays. // 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 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 { let val = PatKind::Deref {
subpattern: Box::new(Pat { subpattern: Box::new(Pat {
kind: PatKind::Array { kind: PatKind::Array {
prefix: tcx prefix: array.unwrap_branch()
.destructure_mir_constant(param_env, array)
.fields
.iter() .iter()
.map(|val| self.recur(*val, false)) .map(|val| self.recur(*val, elem_ty, false))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
slice: None, slice: None,
suffix: Box::new([]), suffix: Box::new([]),
}, },
span, span,
ty: *pointee_ty, ty: tcx.mk_slice(elem_ty),
}), }),
}; };
self.behind_reference.set(old); self.behind_reference.set(old);
@ -353,15 +412,14 @@ impl<'tcx> ConstToPat<'tcx> {
// pattern. // pattern.
ty::Slice(elem_ty) => { ty::Slice(elem_ty) => {
let old = self.behind_reference.replace(true); 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 { let val = PatKind::Deref {
subpattern: Box::new(Pat { subpattern: Box::new(Pat {
kind: PatKind::Slice { kind: PatKind::Slice {
prefix: tcx prefix: array.unwrap_branch()
.destructure_mir_constant(param_env, array)
.fields
.iter() .iter()
.map(|val| self.recur(*val, false)) .map(|val| self.recur(*val, elem_ty, false))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
slice: None, slice: None,
suffix: Box::new([]), suffix: Box::new([]),
@ -415,38 +473,20 @@ impl<'tcx> ConstToPat<'tcx> {
PatKind::Wild PatKind::Wild
} else { } else {
let old = self.behind_reference.replace(true); 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); self.behind_reference.set(old);
PatKind::Deref { subpattern } PatKind::Deref { subpattern }
} }
} }
}, },
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
PatKind::Constant { value: cv } PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) }
}
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);
} }
ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(),
_ => { _ => {
self.saw_const_match_error.set(true); self.saw_const_match_error.set(true);
let err = InvalidPattern { span, non_sm_ty: cv.ty() }; let err = InvalidPattern { span, non_sm_ty: ty };
tcx.sess.emit_err(err); tcx.sess.emit_err(err);
PatKind::Wild PatKind::Wild
} }
@ -460,7 +500,7 @@ impl<'tcx> ConstToPat<'tcx> {
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we // 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. // 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); self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint( tcx.emit_spanned_lint(
@ -471,6 +511,6 @@ impl<'tcx> ConstToPat<'tcx> {
); );
} }
Ok(Box::new(Pat { span, ty: cv.ty(), kind })) Ok(Box::new(Pat { span, ty, kind }))
} }
} }

View file

@ -141,22 +141,21 @@ impl IntRange {
) -> Option<IntRange> { ) -> Option<IntRange> {
let ty = value.ty(); let ty = value.ty();
if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, 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 { let val = match value {
mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => {
// For this specific pattern we can skip a lot of effort and go // For this specific pattern we can skip a lot of effort and go
// straight to the result, after doing a bit of checking. (We // straight to the result, after doing a bit of checking. (We
// could remove this branch and just fall through, which // could remove this branch and just fall through, which
// is more general but much slower.) // is more general but much slower.)
scalar.to_bits_or_ptr_internal(target_size).unwrap().left()? 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");
} }
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. // This is a more general form of the previous case.
value.try_eval_bits(tcx, param_env, ty)? _ => value.try_eval_bits(tcx, param_env, ty),
}; }?;
let val = val ^ bias; let val = val ^ bias;
Some(IntRange { range: val..=val, bias }) Some(IntRange { range: val..=val, bias })
} else { } else {

View file

@ -18,7 +18,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::RangeEnd; use rustc_hir::RangeEnd;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_middle::mir::interpret::{ 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, UserTypeProjection};
use rustc_middle::mir::{BorrowKind, Mutability}; use rustc_middle::mir::{BorrowKind, Mutability};
@ -518,16 +518,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
} }
}; };
// `mir_const_qualif` must be called with the `DefId` of the item where the const is let cid = GlobalId { instance, promoted: None };
// defined, not where it is declared. The difference is significant for associated // Prefer
// constants. let const_value = self
let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq; .tcx
debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); .const_eval_global_id_for_typeck(param_env_reveal_all, cid, Some(span))
.and_then(|val| match val {
Some(valtree) => Ok(mir::ConstantKind::Ty(self.tcx.mk_const(valtree, ty))),
None => self
.tcx
.const_eval_global_id(param_env_reveal_all, cid, Some(span))
.map(|lit| mir::ConstantKind::Val(lit, ty)),
});
match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { match const_value {
Ok(literal) => { Ok(const_) => {
let const_ = mir::ConstantKind::Val(literal, ty); let pattern = self.const_to_pat(const_, id, span, Some(instance.def_id()));
let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation);
if !is_associated_const { if !is_associated_const {
return pattern; return pattern;
@ -578,9 +584,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
span: Span, span: Span,
) -> PatKind<'tcx> { ) -> PatKind<'tcx> {
let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const.def_id); let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const.def_id);
let value = match value {
mir::ConstantKind::Ty(_) => value,
// Evaluate early like we do in `lower_path`. // Evaluate early like we do in `lower_path`.
let value = value.eval(self.tcx, self.param_env); mir::ConstantKind::Unevaluated(ct, ty) => {
let ct = ty::UnevaluatedConst { def: ct.def, substs: ct.substs };
if let Ok(Some(valtree)) =
self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
{
mir::ConstantKind::Ty(self.tcx.mk_const(valtree, ty))
} else {
value.eval(self.tcx, self.param_env)
}
}
mir::ConstantKind::Val(_, _) => unreachable!(),
};
match value { match value {
mir::ConstantKind::Ty(c) => match c.kind() { mir::ConstantKind::Ty(c) => match c.kind() {
@ -591,15 +609,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
ConstKind::Error(_) => { ConstKind::Error(_) => {
return PatKind::Wild; return PatKind::Wild;
} }
_ => bug!("Expected ConstKind::Param"), _ => {}
}, },
mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind, mir::ConstantKind::Val(_, _) => {}
mir::ConstantKind::Unevaluated(..) => { mir::ConstantKind::Unevaluated(..) => {
// If we land here it means the const can't be evaluated because it's `TooGeneric`. // If we land here it means the const can't be evaluated because it's `TooGeneric`.
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span }); self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
return PatKind::Wild; return PatKind::Wild;
} }
} }
self.const_to_pat(value, id, span, None).kind
} }
/// Converts literals, paths and negation of literals to patterns. /// Converts literals, paths and negation of literals to patterns.
@ -626,8 +646,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
let lit_input = let lit_input =
LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) { match self
Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, .tcx
.at(expr.span)
.lit_to_const(lit_input)
.map(mir::ConstantKind::Ty)
.or_else(|_| self.tcx.at(expr.span).lit_to_mir_constant(lit_input))
{
Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, None).kind,
Err(LitToConstError::Reported(_)) => PatKind::Wild, Err(LitToConstError::Reported(_)) => PatKind::Wild,
Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
} }
@ -806,6 +832,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(a)), _a_ty),
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty), mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty),
) => return Some(a.cmp(&b)), ) => return Some(a.cmp(&b)),
(mir::ConstantKind::Ty(a), mir::ConstantKind::Ty(b)) => {
return Some(a.kind().cmp(&b.kind()));
}
_ => {} _ => {}
}, },
} }

View file

@ -17,8 +17,8 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
let literal = constant.literal; let literal = constant.literal;
match literal { match literal {
ConstantKind::Ty(c) => match c.kind() { ConstantKind::Ty(c) => match c.kind() {
ConstKind::Param(_) | ConstKind::Error(_) => {} ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {}
_ => bug!("only ConstKind::Param should be encountered here, got {:#?}", c), _ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
}, },
ConstantKind::Unevaluated(..) => self.required_consts.push(*constant), ConstantKind::Unevaluated(..) => self.required_consts.push(*constant),
ConstantKind::Val(..) => {} ConstantKind::Val(..) => {}

View file

@ -1,7 +1,4 @@
// revisions: cfail1 // revisions: cpass
// should-ice
// error-pattern: forcing query
// known-bug: #101518
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
struct Id<'a> { struct Id<'a> {
@ -9,9 +6,7 @@ struct Id<'a> {
} }
fn visit_struct() { fn visit_struct() {
let id = Id { ns: "random1" }; let id = Id { ns: "random1" };
const FLAG: Id<'static> = Id { const FLAG: Id<'static> = Id { ns: "needs_to_be_the_same" };
ns: "needs_to_be_the_same",
};
match id { match id {
FLAG => {} FLAG => {}
_ => {} _ => {}
@ -19,9 +14,7 @@ fn visit_struct() {
} }
fn visit_struct2() { fn visit_struct2() {
let id = Id { ns: "random2" }; let id = Id { ns: "random2" };
const FLAG: Id<'static> = Id { const FLAG: Id<'static> = Id { ns: "needs_to_be_the_same" };
ns: "needs_to_be_the_same",
};
match id { match id {
FLAG => {} FLAG => {}
_ => {} _ => {}

View file

@ -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>) } // + literal: Const { ty: for<'a, 'b> fn(&'a str, &'b str) -> bool {<str as PartialEq>::eq}, val: Value(<ZST>) }
// mir::Constant // mir::Constant
// + span: $DIR/string.rs:9:14: 9:17 // + span: $DIR/string.rs:9:14: 9:17
// + literal: Const { ty: &str, val: Value(Slice(..)) } // + literal: Const { ty: &str, val: Value(ValTree::Branch(..)) }
} }
bb3: { bb3: {

View file

@ -4,7 +4,7 @@ fn main() {
let a: &dyn Send = &7u32; let a: &dyn Send = &7u32;
match a { match a {
F => panic!(), F => panic!(),
//~^ ERROR `&dyn Send` cannot be used in patterns //~^ ERROR `dyn Send` cannot be used in patterns
_ => {} _ => {}
} }
} }

View file

@ -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 --> $DIR/issue-70972-dyn-trait.rs:6:9
| |
LL | F => panic!(), LL | F => panic!(),

View file

@ -3,6 +3,6 @@ const F: &'static dyn PartialEq<u32> = &7u32;
fn main() { fn main() {
let a: &dyn PartialEq<u32> = &7u32; let a: &dyn PartialEq<u32> = &7u32;
match a { match a {
F => panic!(), //~ ERROR: `&dyn PartialEq<u32>` cannot be used in patterns F => panic!(), //~ ERROR: `dyn PartialEq<u32>` cannot be used in patterns
} }
} }

View file

@ -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 --> $DIR/issue-72565.rs:6:9
| |
LL | F => panic!(), LL | F => panic!(),