Skip to content

exhaustiveness checker mishandles const reference to ADT in match pattern #53708

New issue

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

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

Already on GitHub? # to your account

Closed
hcs64 opened this issue Aug 25, 2018 · 15 comments
Closed

exhaustiveness checker mishandles const reference to ADT in match pattern #53708

hcs64 opened this issue Aug 25, 2018 · 15 comments
Assignees
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns A-MIR Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@hcs64
Copy link

hcs64 commented Aug 25, 2018

Update from @pnkfelix:

Our behavior on this test case (playpen) has changed, but it is still not correct.

#[derive(PartialEq, Eq)]
struct S;

fn main() {
    const C: &S = &S;
    match C {
        C => {}
    }
}

emits error:

error[E0004]: non-exhaustive patterns: `&S` not covered
 --> src/main.rs:6:11
  |
6 |     match C {
  |           ^ pattern `&S` not covered
  |
  = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms

If you work-around the exhaustiveness checker bug by e.g. adding an unreachable arm, then the compiler accepts the code (and as far as I can tell, assigns it the correct runtime semantics).

Original bug report follows:


struct S;

fn main() {
    const C: &S = &S;
    match C {
        C => {}
    }
}

gives:
error: internal compiler error: librustc_mir/hair/pattern/_match.rs:432: bad constructor ConstantValue(Const { ty: &S, val: Scalar(Ptr(Pointer { alloc_id: AllocId(1), offset: Size { raw: 0 } })) }) for adt S

The relevant stack is:

  14: rustc_mir::hair::pattern::_match::Constructor::variant_index_for_adt
  15: rustc_mir::hair::pattern::_match::constructor_sub_pattern_tys
  16: rustc_mir::hair::pattern::_match::is_useful_specialized
  17: <core::iter::Map<I, F> as core::iter::iterator::Iterator>::try_fold
  18: rustc_mir::hair::pattern::_match::is_useful
  19: rustc_mir::hair::pattern::_match::is_useful_specialized
  20: <core::iter::Map<I, F> as core::iter::iterator::Iterator>::try_fold
  21: rustc_mir::hair::pattern::_match::is_useful
  22: rustc_mir::hair::pattern::check_match::check_arms
  23: rustc_mir::hair::pattern::_match::MatchCheckCtxt::create_and_enter
  24: <rustc_mir::hair::pattern::check_match::MatchVisitor<'a, 'tcx> as rustc::hir::intravisit::Visitor<'tcx>>::visit_expr
  25: <rustc_mir::hair::pattern::check_match::MatchVisitor<'a, 'tcx> as rustc::hir::intravisit::Visitor<'tcx>>::visit_expr
  26: <rustc_mir::hair::pattern::check_match::MatchVisitor<'a, 'tcx> as rustc::hir::intravisit::Visitor<'tcx>>::visit_body
  27: rustc::session::Session::track_errors
  28: rustc_mir::hair::pattern::check_match::check_match
  29: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors<'tcx> for rustc::ty::query::queries::check_match<'tcx>>::compute
  30: rustc::dep_graph::graph::DepGraph::with_task_impl
  31: <rustc::ty::query::plumbing::JobOwner<'a, 'tcx, Q>>::start
  32: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::force_query_with_job
  33: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::get_query
  34: rustc::hir::intravisit::Visitor::visit_nested_body
  35: rustc::hir::Crate::visit_all_item_likes
  36: rustc_mir::hair::pattern::check_match::check_crate

It looks like this will take some effort to fix, I patched constructor_sub_pattern_tys to return vec![], but then rustc erroneously reports
error[E0004]: non-exhaustive patterns: '&S' not covered
and with an enum:

enum E {A}

fn main() {
    const C: &E = &E::A;
    match C {
        C => {}
        _ => {}
    }
}

it hits:
error: internal compiler error: librustc\traits\codegen\mod.rs:169: Encountered errors '[FulfillmentError(Obligation(predicate=Binder(TraitPredicate(<E as std::cmp::PartialEq>)),depth=1),Unimplemented)]' resolving bounds after type-checking

@memoryruins memoryruins added the I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ label Aug 25, 2018
@oli-obk
Copy link
Contributor

oli-obk commented Aug 26, 2018

cc @varkor you know this code very well. any pointers?

@varkor
Copy link
Member

varkor commented Aug 26, 2018

Okay, this is actually more interesting than first appears from the example: the matching code dealing with constants behind references is completely broken.

fn main() {
    const C: &bool = &true;
    let x = match C {
        C => "C",
        &true => "true",
        &false => "false",
    };
    println!("{}", x);
}

If you miss off the &true or &false branches, it won't compile, because it doesn't think the match is exhaustive, but if you run it, of course "C" is hit every time.
I think this will only fail when it is exhaustive, rather than thinking it's exhaustive when it's not, so I don't think it's a soundness bug, but something's going very wrong. I'll look into it.

@varkor varkor self-assigned this Aug 26, 2018
@varkor
Copy link
Member

varkor commented Aug 27, 2018

I think the problem here is this:

(&ty::Ref(_, rty, _), &PatternKind::Constant { ref value }) => {
Pattern {
ty: pat.ty,
span: pat.span,
kind: box PatternKind::Deref {
subpattern: Pattern {
ty: rty,
span: pat.span,
kind: box PatternKind::Constant { value: value.clone() },
}
}
}
}

When we encounter a constant, we try to turn it into a pattern, but the handling for references is wrong. We convert it into a deref pattern, but the underlying constant value it refers to still has type &T (and corresponding pointer value), rather than T.
I.e. the box PatternKind::Constant { value: value.clone() } line is incorrect, and should be referring to a dereferenced value instead.

@oli-obk: is there a way to dereference a ty::Const in this way?

@artemshein
Copy link

Just got it in my app. Is there a workaround or matching with constants is just broken for now?

@varkor
Copy link
Member

varkor commented Sep 27, 2018

@artemshein: I would try to avoid using constants in patterns right now; sorry – I haven't got around to fixing this yet.

@oli-obk oli-obk self-assigned this Dec 14, 2018
bors added a commit that referenced this issue Jan 15, 2019
Get rid of the fake stack frame for reading from constants

r? @RalfJung

fixes the ice in #53708 but still keeps around the wrong "non-exhaustive match" error

cc @varkor
Centril added a commit to Centril/rust that referenced this issue Jan 24, 2019
Get rid of the fake stack frame for reading from constants

r? @RalfJung

fixes the ice in rust-lang#53708 but still keeps around the wrong "non-exhaustive match" error

cc @varkor
Centril added a commit to Centril/rust that referenced this issue Jan 24, 2019
Get rid of the fake stack frame for reading from constants

r? @RalfJung

fixes the ice in rust-lang#53708 but still keeps around the wrong "non-exhaustive match" error

cc @varkor
@estebank
Copy link
Contributor

estebank commented Apr 16, 2019

So, there's something that can be done, which is calling report_fulfillment_errors which will in this case emit

error[E0277]: can't compare `S` with `S`
   |
   = help: the trait `std::cmp::PartialEq` is not implemented for `S`
   = note: required because of the requirements on the impl of `std::cmp::PartialEq` for `&S`

I do not know if this is correct though.

@oli-obk
Copy link
Contributor

oli-obk commented Apr 17, 2019

We don't want to report an error on this code. We should be accepting it as correct

@estebank estebank added A-MIR Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html C-bug Category: This is a bug. I-nominated T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 19, 2019
@estebank
Copy link
Contributor

estebank commented Apr 20, 2019

@oli-obk I believe we should be accepting it as correct, but emitting the E0277 instead of an ICE can be a stop gap in the meantime to allow my current abort_if_errors removal effort to plow forward.


Edit: Scratch that, changing the code to be const C: S = S; causes the following error, which seems correct to me, right?

error: to use a constant of type `S` in a pattern, `S` must be annotated with `#[derive(PartialEq, Eq)]`
 --> ../../src/test/ui/consts/match_ice.rs:8:9
  |
8 |         C => {}
  |         ^

If that is the case, then the same error needs to be emitted for &S.

@estebank
Copy link
Contributor

I think I figured how to correctly fix the ICE (by emitting the error in my previous comment) but the exhaustiveness check is still broken.

@estebank estebank added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) and removed I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ I-nominated labels Apr 20, 2019
@oli-obk
Copy link
Contributor

oli-obk commented Apr 20, 2019

won't the ICE reoccur if you add the suggested derives?

@estebank
Copy link
Contributor

In the linked commit I added a test for the struct with the derives and otherwise unchanged and we continue to get the uncovered patterns error, but otherwise things look fine.

estebank added a commit to estebank/rust that referenced this issue Apr 22, 2019
bors added a commit that referenced this issue Apr 23, 2019
Don't stop evaluating due to errors before borrow checking

r? @oli-obk

Fix #60005. Follow up to #59903. Blocked on #53708, fixing the ICE in `src/test/ui/consts/match_ice.rs`.
@pnkfelix pnkfelix changed the title ICE: const reference to ADT in match pattern rustc erroneously rejects const reference to ADT in match pattern Jun 6, 2019
@pnkfelix pnkfelix changed the title rustc erroneously rejects const reference to ADT in match pattern exhaustiveness checker mishandles const reference to ADT in match pattern Jun 6, 2019
@pnkfelix
Copy link
Member

pnkfelix commented Jun 6, 2019

triage: P-medium.

@Nadrieril
Copy link
Member

This appears to be fixed on nightly, probably due to #70743

@oli-obk oli-obk closed this as completed Oct 21, 2020
@varkor varkor added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Oct 21, 2020
@varkor
Copy link
Member

varkor commented Oct 21, 2020

(If there's already a test for this case, feel free to close again: I just want to be sure.)

Nadrieril added a commit to Nadrieril/rust that referenced this issue Oct 21, 2020
This issue was accidentally fixed recently, probably by rust-lang#70743
@Nadrieril
Copy link
Member

There wasn't. I just added one in #78072

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns A-MIR Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants