Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

match expression falling through?

Update: this is an old bug solved in 1.12


Here is some contrived but simple pattern matching example (demo):

fn main() {
    let x = 'a';

    match x {
            'a'...'b' if false => {
                println!("one");
            },

            'a' => {
                println!("two");
            },

            'a'...'b' => {
                println!("three");
            },

            _ => panic!("what?")
    }
}

When I run it, I get three as output. Why is this happening? Shouldn't the second branch match first?

like image 241
rvidal Avatar asked Jun 12 '15 17:06

rvidal


People also ask

What does something falling through mean?

: to fail or stop in a sudden or final way Contract negotiations have fallen through. Our vacation plans have fallen through.

What is a matching expression?

A match expression branches on a pattern. The exact form of matching that occurs depends on the pattern. A match expression has a scrutinee expression, which is the value to compare to the patterns. The scrutinee expression and the patterns must have the same type.

What does falling through the crack mean?

: to fail to be noticed, assisted, or included with others Parents are concerned that children who have trouble in school will fall through the cracks in the school system. The program is meant to help workers who may have slipped through the cracks because of their age.

Where does fall through the cracks come from?

The expressions fall through the cracks and slip through the cracks came into use in the mid-twentieth century. The exact origin is unknown, but it reflects the idea that something that should have been contained or captured somehow seeped away, unnoticed.


1 Answers

Looking at the LLVM IR in Debug, it is already flawed, so this is definitely a bug in rustc; we'll use the curated IR below to check what's going on.

So, %x is assigned 'a' (97 in ASCII), and %10 is assigned the result of x >= 'a' and x <= 'b'; if this is true, we go to match_case, otherwise to compare_next. match_case redirect to cond, which redirects to case_body2 which prints "three".

In theory, we would have wanted to go to case_body1 (printing "two"), from cond7, from match_case4, from compare_next. But compare_next is only reached if x is not in ['a', 'b'] according to the IR.

This clearly looks like a bug.

; Function Attrs: uwtable
define internal void @_ZN4main20h4f7b0d7962de19d8eaaE() unnamed_addr #0 {
entry-block:
  %x = alloca i32
  ; [...]
  store i32 97, i32* %x
  %7 = load i32* %x, !range !0
  %8 = icmp uge i32 %7, 97
  %9 = icmp ule i32 %7, 98
  %10 = and i1 %8, %9
  br i1 %10, label %match_case, label %compare_next

case_body:                                        ; preds = %next6, %next
  ; println!("one")
  br label %case_body8

case_body1:                                       ; preds = %cond7
  ; println!("two")
  br label %case_body10

case_body2:                                       ; preds = %cond
  ; println!("three")
  br label %case_body15

case_body3:                                       ; preds = %match_else
  ; panic!("what")
  unreachable

match_else:                                       ; preds = %compare_next5
  br label %case_body3

match_case:                                       ; preds = %entry-block
  br i1 true, label %cond, label %next

compare_next:                                     ; preds = %entry-block
  %16 = icmp eq i32 %7, 97
  br i1 %16, label %match_case4, label %compare_next5

next:                                             ; preds = %match_case
  br label %case_body

cond:                                             ; preds = %match_case
  br label %case_body2

match_case4:                                      ; preds = %compare_next
  br i1 true, label %cond7, label %next6

; [...]

cond7:                                            ; preds = %match_case4
  br label %case_body1

; [...]
}
like image 113
Matthieu M. Avatar answered Oct 01 '22 14:10

Matthieu M.