rollup merge of #23863: pnkfelix/arith-oflo-const-eval

const_eval : add overflow-checking for {`+`, `-`, `*`, `/`, `<<`, `>>`}.

One tricky detail here: There is some duplication of labor between `rustc::middle::const_eval` and `rustc_trans::trans::consts`. It might be good to explore ways to try to factor out the common structure to the two passes (by abstracting over the particular value-representation used in the compile-time interpreter).

----

Update: Rebased atop #23841

Fix #22531

Fix #23030

Fix #23221

Fix #23235
This commit is contained in:
Alex Crichton 2015-03-31 18:06:35 -07:00
commit 4f643d79fc
30 changed files with 1733 additions and 267 deletions

View file

@ -0,0 +1,30 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Evaluation of constants in refutable patterns goes through
// different compiler control-flow paths.
#![allow(unused_imports)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const NEG_128: i8 = -128;
const NEG_NEG_128: i8 = -NEG_128;
//~^ ERROR constant evaluation error: attempted to negate with overflow
//~| ERROR attempted to negate with overflow
fn main() {
match -128i8 {
NEG_NEG_128 => println!("A"),
_ => println!("B"),
}
}

View file

@ -0,0 +1,39 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Evaluation of constants in array-elem count goes through different
// compiler control-flow paths.
//
// This test is checking the count in an array expression.
// FIXME (#23926): the error output is not consistent between a
// self-hosted and a cross-compiled setup; therefore resorting to
// error-pattern for now.
// error-pattern: expected constant integer for repeat count, but attempted to add with overflow
#![allow(unused_imports)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const A_I8_I
: [u32; (i8::MAX as usize) + 1]
= [0; (i8::MAX + 1) as usize];
fn main() {
foo(&A_I8_I[..]);
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}

View file

@ -0,0 +1,43 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Evaluation of constants in array-elem count goes through different
// compiler control-flow paths.
//
// This test is checking the count in an array expression.
//
// This is a variation of another such test, but in this case the
// types for the left- and right-hand sides of the addition do not
// match (as well as overflow).
// FIXME (#23926): the error output is not consistent between a
// self-hosted and a cross-compiled setup; therefore resorting to
// error-pattern for now.
// error-pattern: mismatched types
#![allow(unused_imports)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const A_I8_I
: [u32; (i8::MAX as usize) + 1]
= [0; (i8::MAX + 1u8) as usize];
fn main() {
foo(&A_I8_I[..]);
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}

View file

@ -0,0 +1,36 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-test this should fail to compile (#23833)
// Evaluation of constants in array-elem count goes through different
// compiler control-flow paths.
//
// This test is checking the count in an array type.
#![allow(unused_imports)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const A_I8_T
: [u32; (i8::MAX as i8 + 1i8) as usize]
//~^ ERROR error evaluating count: attempted to add with overflow
= [0; (i8::MAX as usize) + 1];
fn main() {
foo(&A_I8_T[..]);
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}

View file

@ -0,0 +1,36 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Evaluation of constants in array-elem count goes through different
// compiler control-flow paths.
//
// This test is checking the count in an array type.
#![allow(unused_imports)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const A_I8_T
: [u32; (i8::MAX as i8 + 1u8) as usize]
//~^ ERROR mismatched types
//~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
//~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
= [0; (i8::MAX as usize) + 1];
fn main() {
foo(&A_I8_T[..]);
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}

View file

@ -0,0 +1,129 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(unused_imports)]
// Note: the relevant lint pass here runs before some of the constant
// evaluation below (e.g. that performed by trans and llvm), so if you
// change this warn to a deny, then the compiler will exit before
// those errors are detected.
#![warn(unsigned_negation)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const VALS_I8: (i8, i8, i8, i8) =
(-i8::MIN,
//~^ ERROR attempted to negate with overflow
i8::MIN - 1,
//~^ ERROR attempted to sub with overflow
i8::MAX + 1,
//~^ ERROR attempted to add with overflow
i8::MIN * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_I16: (i16, i16, i16, i16) =
(-i16::MIN,
//~^ ERROR attempted to negate with overflow
i16::MIN - 1,
//~^ ERROR attempted to sub with overflow
i16::MAX + 1,
//~^ ERROR attempted to add with overflow
i16::MIN * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_I32: (i32, i32, i32, i32) =
(-i32::MIN,
//~^ ERROR attempted to negate with overflow
i32::MIN - 1,
//~^ ERROR attempted to sub with overflow
i32::MAX + 1,
//~^ ERROR attempted to add with overflow
i32::MIN * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_I64: (i64, i64, i64, i64) =
(-i64::MIN,
//~^ ERROR attempted to negate with overflow
i64::MIN - 1,
//~^ ERROR attempted to sub with overflow
i64::MAX + 1,
//~^ ERROR attempted to add with overflow
i64::MAX * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_U8: (u8, u8, u8, u8) =
(-u8::MIN,
//~^ WARNING negation of unsigned int variable may be unintentional
// (The above is separately linted; unsigned negation is defined to be !x+1.)
u8::MIN - 1,
//~^ ERROR attempted to sub with overflow
u8::MAX + 1,
//~^ ERROR attempted to add with overflow
u8::MAX * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_U16: (u16, u16, u16, u16) =
(-u16::MIN,
//~^ WARNING negation of unsigned int variable may be unintentional
// (The above is separately linted; unsigned negation is defined to be !x+1.)
u16::MIN - 1,
//~^ ERROR attempted to sub with overflow
u16::MAX + 1,
//~^ ERROR attempted to add with overflow
u16::MAX * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_U32: (u32, u32, u32, u32) =
(-u32::MIN,
//~^ WARNING negation of unsigned int variable may be unintentional
// (The above is separately linted; unsigned negation is defined to be !x+1.)
u32::MIN - 1,
//~^ ERROR attempted to sub with overflow
u32::MAX + 1,
//~^ ERROR attempted to add with overflow
u32::MAX * 2,
//~^ ERROR attempted to mul with overflow
);
const VALS_U64: (u64, u64, u64, u64) =
(-u64::MIN,
//~^ WARNING negation of unsigned int variable may be unintentional
// (The above is separately linted; unsigned negation is defined to be !x+1.)
u64::MIN - 1,
//~^ ERROR attempted to sub with overflow
u64::MAX + 1,
//~^ ERROR attempted to add with overflow
u64::MAX * 2,
//~^ ERROR attempted to mul with overflow
);
fn main() {
foo(VALS_I8);
foo(VALS_I16);
foo(VALS_I32);
foo(VALS_I64);
foo(VALS_U8);
foo(VALS_U16);
foo(VALS_U32);
foo(VALS_U64);
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}

View file

@ -0,0 +1,118 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// When explicit discriminant value has
// a type that does not match the representation
// type, rustc should fail gracefully.
// See also run-pass/discrim-explicit-23030.rs where the input types
// are correct.
#![allow(dead_code, unused_variables, unused_imports)]
use std::{i8,u8,i16,u16,i32,u32,i64, u64};
fn f_i8() {
#[repr(i8)]
enum A {
Ok = i8::MAX - 1,
Ok2,
OhNo = 0_u8,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_u8() {
#[repr(u8)]
enum A {
Ok = u8::MAX - 1,
Ok2,
OhNo = 0_i8,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_i16() {
#[repr(i16)]
enum A {
Ok = i16::MAX - 1,
Ok2,
OhNo = 0_u16,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_u16() {
#[repr(u16)]
enum A {
Ok = u16::MAX - 1,
Ok2,
OhNo = 0_i16,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_i32() {
#[repr(i32)]
enum A {
Ok = i32::MAX - 1,
Ok2,
OhNo = 0_u32,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_u32() {
#[repr(u32)]
enum A {
Ok = u32::MAX - 1,
Ok2,
OhNo = 0_i32,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_i64() {
#[repr(i64)]
enum A {
Ok = i64::MAX - 1,
Ok2,
OhNo = 0_u64,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn f_u64() {
#[repr(u64)]
enum A {
Ok = u64::MAX - 1,
Ok2,
OhNo = 0_i64,
//~^ ERROR mismatched types
}
let x = A::Ok;
}
fn main() { }

View file

@ -0,0 +1,94 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// Issue 23030: Detect overflowing discriminant
//
// Check that we detect the overflow even if enum is not used.
// See also run-pass/discrim-explicit-23030.rs where the suggested
// workaround is tested.
use std::{i8,u8,i16,u16,i32,u32,i64, u64};
fn f_i8() {
#[repr(i8)]
enum A {
Ok = i8::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed on value after 127: i8; set explicitly via OhNo = -128 if that is desired outcome
}
}
fn f_u8() {
#[repr(u8)]
enum A {
Ok = u8::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed on value after 255: u8; set explicitly via OhNo = 0 if that is desired outcome
}
}
fn f_i16() {
#[repr(i16)]
enum A {
Ok = i16::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
}
fn f_u16() {
#[repr(u16)]
enum A {
Ok = u16::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
}
fn f_i32() {
#[repr(i32)]
enum A {
Ok = i32::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
}
fn f_u32() {
#[repr(u32)]
enum A {
Ok = u32::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
}
fn f_i64() {
#[repr(i64)]
enum A {
Ok = i64::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
}
fn f_u64() {
#[repr(u64)]
enum A {
Ok = u64::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
}
fn main() { }

View file

@ -0,0 +1,108 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// Issue 23030: Detect overflowing discriminant
// See also run-pass/discrim-explicit-23030.rs where the suggested
// workaround is tested.
use std::{i8,u8,i16,u16,i32,u32,i64, u64};
fn f_i8() {
#[repr(i8)]
enum A {
Ok = i8::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed on value after 127: i8; set explicitly via OhNo = -128 if that is desired outcome
}
let x = A::Ok;
}
fn f_u8() {
#[repr(u8)]
enum A {
Ok = u8::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed on value after 255: u8; set explicitly via OhNo = 0 if that is desired outcome
}
let x = A::Ok;
}
fn f_i16() {
#[repr(i16)]
enum A {
Ok = i16::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
let x = A::Ok;
}
fn f_u16() {
#[repr(u16)]
enum A {
Ok = u16::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
let x = A::Ok;
}
fn f_i32() {
#[repr(i32)]
enum A {
Ok = i32::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
let x = A::Ok;
}
fn f_u32() {
#[repr(u32)]
enum A {
Ok = u32::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
let x = A::Ok;
}
fn f_i64() {
#[repr(i64)]
enum A {
Ok = i64::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
let x = A::Ok;
}
fn f_u64() {
#[repr(u64)]
enum A {
Ok = u64::MAX - 1,
Ok2,
OhNo, //~ ERROR enum discriminant overflowed
}
let x = A::Ok;
}
fn main() { }

View file

@ -8,9 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: too big for the current
// FIXME (#23926): the error output is not consistent between a
// self-hosted and a cross-compiled setup. Skipping for now.
// ignore-test FIXME(#23926)
#![allow(exceeding_bitshifts)]
fn main() {
let fat : [u8; (1<<61)+(1<<31)] = [0; (1u64<<61) as usize +(1u64<<31) as usize];
let _fat : [u8; (1<<61)+(1<<31)] =
[0; (1u64<<61) as usize +(1u64<<31) as usize];
}

View file

@ -8,9 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Note: This test is checking that we forbid a coding pattern that
// Issue #5873 explicitly wants to allow.
enum State { ST_NULL, ST_WHITESPACE }
fn main() {
[State::ST_NULL; (State::ST_WHITESPACE as usize)];
//~^ ERROR expected constant integer for repeat count, found non-constant expression
//~^ ERROR expected constant integer for repeat count, but non-constant path
}

View file

@ -12,6 +12,7 @@
fn main() {
fn bar(n: usize) {
let _x = [0; n]; //~ ERROR expected constant integer for repeat count, found variable
let _x = [0; n];
//~^ ERROR expected constant integer for repeat count, found variable
}
}

View file

@ -10,11 +10,16 @@
// pretty-expanded FIXME #23616
#![feature(core)]
// Catch mistakes in the overflowing literals lint.
#![deny(overflowing_literals)]
pub fn main() {
assert_eq!(0xffffffff, (-1 as u32));
assert_eq!(4294967295, (-1 as u32));
assert_eq!(0xffffffffffffffff, (-1 as u64));
assert_eq!(18446744073709551615, (-1 as u64));
assert_eq!(-2147483648 - 1, 2147483647);
assert_eq!((-2147483648).wrapping_sub(1), 2147483647);
}

View file

@ -0,0 +1,156 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Issue 23030: Workaround overflowing discriminant
// with explicit assignments.
// See also compile-fail/overflow-discrim.rs, which shows what
// happens if you leave the OhNo explicit cases out here.
use std::{i8,u8,i16,u16,i32,u32,i64,u64,isize,usize};
fn f_i8() {
#[repr(i8)]
enum A {
Ok = i8::MAX - 1,
Ok2,
OhNo = i8::MIN,
NotTheEnd = -1,
Zero,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
let z = (A::NotTheEnd, A::Zero).1 as i8;
assert_eq!(z, 0);
}
fn f_u8() {
#[repr(u8)]
enum A {
Ok = u8::MAX - 1,
Ok2,
OhNo = u8::MIN,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
}
fn f_i16() {
#[repr(i16)]
enum A {
Ok = i16::MAX - 1,
Ok2,
OhNo = i16::MIN,
NotTheEnd = -1,
Zero,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
let z = (A::NotTheEnd, A::Zero).1 as i16;
assert_eq!(z, 0);
}
fn f_u16() {
#[repr(u16)]
enum A {
Ok = u16::MAX - 1,
Ok2,
OhNo = u16::MIN,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
}
fn f_i32() {
#[repr(i32)]
enum A {
Ok = i32::MAX - 1,
Ok2,
OhNo = i32::MIN,
NotTheEnd = -1,
Zero,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
let z = (A::NotTheEnd, A::Zero).1 as i32;
assert_eq!(z, 0);
}
fn f_u32() {
#[repr(u32)]
enum A {
Ok = u32::MAX - 1,
Ok2,
OhNo = u32::MIN,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
}
fn f_i64() {
#[repr(i64)]
enum A {
Ok = i64::MAX - 1,
Ok2,
OhNo = i64::MIN,
NotTheEnd = -1,
Zero,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
let z = (A::NotTheEnd, A::Zero).1 as i64;
assert_eq!(z, 0);
}
fn f_u64() {
#[repr(u64)]
enum A {
Ok = u64::MAX - 1,
Ok2,
OhNo = u64::MIN,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
}
fn f_isize() {
#[repr(isize)]
enum A {
Ok = isize::MAX - 1,
Ok2,
OhNo = isize::MIN,
NotTheEnd = -1,
Zero,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
let z = (A::NotTheEnd, A::Zero).1 as isize;
assert_eq!(z, 0);
}
fn f_usize() {
#[repr(usize)]
enum A {
Ok = usize::MAX - 1,
Ok2,
OhNo = usize::MIN,
}
let _x = (A::Ok, A::Ok2, A::OhNo);
}
fn main() {
f_i8(); f_u8();
f_i16(); f_u16();
f_i32(); f_u32();
f_i64(); f_u64();
f_isize(); f_usize();
}

View file

@ -10,6 +10,9 @@
// pretty-expanded FIXME #23616
// this is for the wrapping_add call below.
#![feature(core)]
/*!
* Tests the range assertion wraparound case in trans::middle::adt::load_discr.
*/
@ -29,8 +32,8 @@ static CLs: Es = Es::Ls;
static CHs: Es = Es::Hs;
pub fn main() {
assert_eq!((Eu::Hu as u8) + 1, Eu::Lu as u8);
assert_eq!((Es::Hs as i8) + 1, Es::Ls as i8);
assert_eq!((Eu::Hu as u8).wrapping_add(1), Eu::Lu as u8);
assert_eq!((Es::Hs as i8).wrapping_add(1), Es::Ls as i8);
assert_eq!(CLu as u8, Eu::Lu as u8);
assert_eq!(CHu as u8, Eu::Hu as u8);
assert_eq!(CLs as i8, Es::Ls as i8);