Skip to content

Add context to "const in pattern" errors #133233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
On const pattern errors, point at the const item definition
Centralize emitting an error in `const_to_pat` so that all errors from that evaluating a `const` in a pattern can add addditional information. With this, now point at the `const` item's definition:

```
error[E0158]: constant pattern depends on a generic parameter
  --> $DIR/associated-const-type-parameter-pattern.rs:20:9
   |
LL | pub trait Foo {
   | -------------
LL |     const X: EFoo;
   |     ------------- constant defined here
...
LL |         A::X => println!("A::X"),
   |         ^^^^
```
  • Loading branch information
estebank committed Dec 4, 2024
commit c6205055e085a1210c1503978225d34147a0d8bd
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =

mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable

mir_build_const_defined_here = constant defined here

mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns

mir_build_const_pattern_depends_on_generic_parameter =
Expand Down
103 changes: 70 additions & 33 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_apfloat::Float;
use rustc_errors::{Diag, PResult};
use rustc_hir as hir;
use rustc_index::Idx;
use rustc_infer::infer::TyCtxtInferExt;
Expand Down Expand Up @@ -35,11 +36,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
id: hir::HirId,
span: Span,
) -> Box<Pat<'tcx>> {
let mut convert = ConstToPat::new(self, id, span);
let mut convert = ConstToPat::new(self, id, span, c);

match c.kind() {
ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty),
ty::ConstKind::Value(_, val) => match convert.valtree_to_pat(val, ty) {
Ok(pat) => pat,
Err(err) => convert.mk_err(err, ty),
},
_ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
}
}
Expand All @@ -51,10 +55,12 @@ struct ConstToPat<'tcx> {
span: Span,

treat_byte_string_as_slice: bool,

c: ty::Const<'tcx>,
}

impl<'tcx> ConstToPat<'tcx> {
fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self {
fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
trace!(?pat_ctxt.typeck_results.hir_owner);
ConstToPat {
tcx: pat_ctxt.tcx,
Expand All @@ -64,20 +70,40 @@ impl<'tcx> ConstToPat<'tcx> {
.typeck_results
.treat_byte_string_as_slice
.contains(&id.local_id),
c,
}
}

fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
ty.is_structural_eq_shallow(self.tcx)
}

/// We errored. Signal that in the pattern, so that follow up errors can be silenced.
fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
let def_kind = self.tcx.def_kind(uv.def);
if let hir::def::DefKind::AssocConst = def_kind
&& let Some(def_id) = uv.def.as_local()
{
// Include the container item in the output.
err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
}
if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
err.span_label(
self.tcx.def_span(uv.def),
crate::fluent_generated::mir_build_const_defined_here,
);
}
}
Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
}

fn unevaluated_to_pat(
&mut self,
uv: ty::UnevaluatedConst<'tcx>,
ty: Ty<'tcx>,
) -> Box<Pat<'tcx>> {
trace!(self.treat_byte_string_as_slice);
let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind });

// It's not *technically* correct to be revealing opaque types here as borrowcheck has
// not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
Expand All @@ -96,44 +122,46 @@ impl<'tcx> ConstToPat<'tcx> {
Ok(Ok(c)) => c,
Err(ErrorHandled::Reported(_, _)) => {
// Let's tell the use where this failing const occurs.
let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span });
return pat_from_kind(PatKind::Error(e));
let err = self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
return self.mk_err(err, ty);
}
Err(ErrorHandled::TooGeneric(_)) => {
let e = self
.tcx
.dcx()
.emit_err(ConstPatternDependsOnGenericParameter { span: self.span });
return pat_from_kind(PatKind::Error(e));
.create_err(ConstPatternDependsOnGenericParameter { span: self.span });
return self.mk_err(e, ty);
}
Ok(Err(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 })
self.tcx.dcx().create_err(UnionPattern { span: self.span })
}
ty::FnPtr(..) | ty::RawPtr(..) => {
self.tcx.dcx().emit_err(PointerPattern { span: self.span })
self.tcx.dcx().create_err(PointerPattern { span: self.span })
}
_ => self
.tcx
.dcx()
.emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }),
.create_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }),
};
return pat_from_kind(PatKind::Error(e));
return self.mk_err(e, ty);
}
};

// Convert the valtree to a const.
let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
let inlined_const_as_pat = match self.valtree_to_pat(valtree, ty) {
Ok(pat) => pat,
Err(err) => self.mk_err(err, ty),
};

if !inlined_const_as_pat.references_error() {
// Always check for `PartialEq` if we had no other errors yet.
if !self.type_has_partial_eq_impl(ty) {
let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty };
let e = self.tcx.dcx().emit_err(err);
return pat_from_kind(PatKind::Error(e));
return self.mk_err(self.tcx.dcx().create_err(err), ty);
}
}

Expand Down Expand Up @@ -175,14 +203,20 @@ impl<'tcx> ConstToPat<'tcx> {
let field = FieldIdx::new(idx);
// Patterns can only use monomorphic types.
let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
FieldPat { field, pattern: self.valtree_to_pat(val, ty) }
FieldPat {
field,
pattern: match self.valtree_to_pat(val, ty) {
Ok(pat) => pat,
Err(err) => self.mk_err(err, ty),
},
}
})
.collect()
}

// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
#[instrument(skip(self), level = "debug")]
fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> PResult<'_, Box<Pat<'tcx>>> {
let span = self.span;
let tcx = self.tcx;
let kind = match ty.kind() {
Expand All @@ -191,9 +225,7 @@ impl<'tcx> ConstToPat<'tcx> {
// 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);
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
PatKind::Error(e)
return Err(tcx.dcx().create_err(err));
}
ty::Adt(adt_def, args) if adt_def.is_enum() => {
let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
Expand Down Expand Up @@ -227,7 +259,10 @@ impl<'tcx> ConstToPat<'tcx> {
prefix: cv
.unwrap_branch()
.iter()
.map(|val| self.valtree_to_pat(*val, *elem_ty))
.map(|val| match self.valtree_to_pat(*val, *elem_ty) {
Ok(pat) => pat,
Err(err) => self.mk_err(err, ty),
})
.collect(),
slice: None,
suffix: Box::new([]),
Expand All @@ -236,7 +271,10 @@ impl<'tcx> ConstToPat<'tcx> {
prefix: cv
.unwrap_branch()
.iter()
.map(|val| self.valtree_to_pat(*val, *elem_ty))
.map(|val| match self.valtree_to_pat(*val, *elem_ty) {
Ok(pat) => pat,
Err(err) => self.mk_err(err, ty),
})
.collect(),
slice: None,
suffix: Box::new([]),
Expand All @@ -252,10 +290,9 @@ impl<'tcx> ConstToPat<'tcx> {
// deref pattern.
_ => {
if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
let e = tcx.dcx().emit_err(err);
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
PatKind::Error(e)
return Err(tcx
.dcx()
.create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }));
} else {
// `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.
Expand All @@ -270,7 +307,10 @@ impl<'tcx> ConstToPat<'tcx> {
_ => *pointee_ty,
};
// References have the same valtree representation as their pointee.
let subpattern = self.valtree_to_pat(cv, pointee_ty);
let subpattern = match self.valtree_to_pat(cv, pointee_ty) {
Ok(pat) => pat,
Err(err) => self.mk_err(err, ty),
};
PatKind::Deref { subpattern }
}
}
Expand All @@ -286,8 +326,7 @@ impl<'tcx> ConstToPat<'tcx> {
if is_nan {
// NaNs are not ever equal to anything so they make no sense as patterns.
// Also see <https://github.com/rust-lang/rfcs/pull/3535>.
let e = tcx.dcx().emit_err(NaNPattern { span });
PatKind::Error(e)
return Err(tcx.dcx().create_err(NaNPattern { span }));
} else {
PatKind::Constant {
value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
Expand All @@ -306,12 +345,10 @@ impl<'tcx> ConstToPat<'tcx> {
}
_ => {
let err = InvalidPattern { span, non_sm_ty: ty };
let e = tcx.dcx().emit_err(err);
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
PatKind::Error(e)
return Err(tcx.dcx().create_err(err));
}
};

Box::new(Pat { span, ty, kind })
Ok(Box::new(Pat { span, ty, kind }))
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
error[E0158]: constant pattern depends on a generic parameter
--> $DIR/associated-const-type-parameter-pattern.rs:20:9
|
LL | pub trait Foo {
| -------------
LL | const X: EFoo;
| ------------- constant defined here
...
LL | A::X => println!("A::X"),
| ^^^^

error[E0158]: constant pattern depends on a generic parameter
--> $DIR/associated-const-type-parameter-pattern.rs:22:9
|
LL | pub trait Foo {
| -------------
LL | const X: EFoo;
| ------------- constant defined here
...
LL | B::X => println!("B::X"),
| ^^^^

error[E0158]: constant pattern depends on a generic parameter
--> $DIR/associated-const-type-parameter-pattern.rs:30:9
|
LL | pub trait Foo {
| -------------
LL | const X: EFoo;
| ------------- constant defined here
...
LL | let A::X = arg;
| ^^^^

error[E0158]: constant pattern depends on a generic parameter
--> $DIR/associated-const-type-parameter-pattern.rs:28:48
|
LL | pub trait Foo {
| -------------
LL | const X: EFoo;
| ------------- constant defined here
...
LL | pub fn test_let_pat<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
| ^^^^

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ LL | pub struct Opcode2<S>(&'a S);
error: could not evaluate constant pattern
--> $DIR/ice-type-mismatch-when-copying-112824.rs:15:9
|
LL | impl Opcode2 {
| ------------
LL | pub const OP2: Opcode2 = Opcode2(Opcode(0x1));
| ---------------------- constant defined here
...
LL | Opcode2::OP2 => unimplemented!(),
| ^^^^^^^^^^^^

Expand Down
3 changes: 3 additions & 0 deletions tests/ui/consts/const-eval/const-eval-overflow-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ LL | const NEG_NEG_128: i8 = -NEG_128;
error: could not evaluate constant pattern
--> $DIR/const-eval-overflow-2.rs:15:9
|
LL | const NEG_NEG_128: i8 = -NEG_128;
| --------------------- constant defined here
...
LL | NEG_NEG_128 => println!("A"),
| ^^^^^^^^^^^

Expand Down
3 changes: 3 additions & 0 deletions tests/ui/consts/const-eval/ref_to_int_match.64bit.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ error: could not evaluate constant pattern
|
LL | 10..=BAR => {},
| ^^^
...
LL | const BAR: Int = unsafe { Foo { r: &42 }.f };
| -------------- constant defined here

error: aborting due to 2 previous errors

Expand Down
10 changes: 10 additions & 0 deletions tests/ui/consts/const_in_pattern/cross-crate-fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be ann
LL | consts::SOME => panic!(),
| ^^^^^^^^^^^^
|
::: $DIR/auxiliary/consts.rs:11:1
|
LL | pub const SOME: Option<CustomEq> = Some(CustomEq);
| -------------------------------- constant defined here
|
= 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

Expand All @@ -13,6 +18,11 @@ error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be ann
LL | <Defaulted as consts::AssocConst>::SOME => panic!(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: $DIR/auxiliary/consts.rs:15:5
|
LL | const SOME: Option<CustomEq> = Some(CustomEq);
| ---------------------------- constant defined here
|
= 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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
--> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:9:9
|
LL | const C: *const u8 = &0;
| ------------------ constant defined here
...
LL | C => {}
| ^

error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
--> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:16:9
|
LL | const C_INNER: (*const u8, u8) = (C, 0);
| ------------------------------ constant defined here
...
LL | C_INNER => {}
| ^^^^^^^

error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
--> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:27:9
|
LL | const D: *const [u8; 4] = b"abcd";
| ----------------------- constant defined here
...
LL | D => {}
| ^

error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
--> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:32:9
|
LL | const STR: *const str = "abcd";
| --------------------- constant defined here
...
LL | STR => {}
| ^^^

Expand Down
Loading