Description
For most patterns and in safe code, "evaluation"(/matching?) order of subpatterns does not matter, but there is (that I can think of) one instance on stable where pattern evaluation order matters: matching on a struct with a tag and a union
field (and similar situations).
The Reference section on union
s does mention pattern matching, but does not say anything about pattern evaluation order. It gives an example of pattern-matching on a manual tagged union, though pattern evaluation order does not matter for the example given1. In a slightly different example, however, the field order does matter:
my original example
#[derive(Clone, Copy)]
enum Tag { A, B, }
#[derive(Clone, Copy)]
#[repr(C)]
union Value {
a: u32,
b: u8, // note that b is smaller than a
}
/// Assume that if tag == Tag::A, then val.a is valid, and if tag == Tag::B, then tag.b is valid.
#[derive(Clone, Copy)]
struct Tagged {
tag: Tag,
val: Value,
}
unsafe fn tag_first(v: Tagged) -> bool {
match v {
// fine under miri with tag == B, sees that `tag != A` and skips the arm
Tagged { tag: Tag::A, val: Value { a: 0 } } => true,
_ => false,
}
}
unsafe fn val_first(v: Tagged) -> bool {
match v {
// error under miri with tag == B, since it reads the padding bytes after `Value::b`
Tagged { val: Value { a: 0 }, tag: Tag::A } => true,
_ => false,
}
}
fn main() {
let v = Tagged {
tag: Tag::B,
val: Value { b: 0 },
};
unsafe {
tag_first(v);
val_first(v);
}
}
a simpler but basically the same example
fn main() {
union Union { value: u8, _empty: () }
struct MyOption { tag: u8, value: Union } // assume tag == 1 means value.value is valid
let foo = MyOption { tag: 0, value: Union { _empty: () } };
unsafe {
match foo {
// currently fine under Miri, since `tag` is mentioned first
MyOption { tag: 1, value: Union { value: 0 } } => true,
_ => false,
};
match foo {
// currently this is UB under Miri if value is `_empty`/uninit, regardless of the tag field
MyOption { value: Union { value: 0 }, tag: 1 } => true,
_ => false,
};
}
}
For unstable code, I suppose deref_patterns
might also make it important to document pattern evaluation order, or maybe that feature is/will be restricted enough for it not to matter. Depending on the resolution of #412 pattern evaluation order might be important if matching on references-to-references-to-invalid-data (miri example)?
I'm not sure if this is fully the intended behavior2, or if it is intended, how best to document it.
Footnotes
-
in that example, the union field is fully-initailized either way, or UB happens regardless of pattern evaluation order ↩
-
Alternately, instead of documenting pattern evaluation order, it could be specified that if any (union) field used in a pattern match is invalid/uninitialized, then the whole arm is UB, regardless of the order the fields were written in the pattern. ↩