// check-pass
// aux-build:empty.rs
//
// This tests plays with matching and uninhabited types. This also serves as a test for the
// `Ty::is_inhabited_from` function.
#![feature(never_type)]
#![feature(never_type_fallback)]
#![feature(exhaustive_patterns)]
#![deny(unreachable_patterns)]

macro_rules! assert_empty {
    ($ty:ty) => {
        const _: () = {
            fn assert_empty(x: $ty) {
                match x {}
                match Some(x) {
                    None => {}
                }
            }
        };
    };
}
macro_rules! assert_non_empty {
    ($ty:ty) => {
        const _: () = {
            fn assert_non_empty(x: $ty) {
                match x {
                    _ => {}
                }
                match Some(x) {
                    None => {}
                    Some(_) => {}
                }
            }
        };
    };
}

extern crate empty;
assert_empty!(empty::EmptyForeignEnum);
assert_empty!(empty::VisiblyUninhabitedForeignStruct);
assert_non_empty!(empty::SecretlyUninhabitedForeignStruct);

enum Void {}
assert_empty!(Void);

enum Enum2 {
    Foo(Void),
    Bar(!),
}
assert_empty!(Enum2);

enum Enum3 {
    Foo(Void),
    Bar {
        x: u64,
        y: !,
    },
}
assert_empty!(Enum3);

enum Enum4 {
    Foo(u64),
    Bar(!),
}
assert_non_empty!(Enum4);

struct Struct1(empty::EmptyForeignEnum);
assert_empty!(Struct1);

struct Struct2 {
    x: u64,
    y: !,
}
assert_empty!(Struct2);

union Union {
    foo: !,
}
assert_non_empty!(Union);

assert_empty!((!, String));

assert_non_empty!(&'static !);
assert_non_empty!(&'static Struct1);
assert_non_empty!(&'static &'static &'static !);

assert_empty!([!; 1]);
assert_empty!([Void; 2]);
assert_non_empty!([!; 0]);
assert_non_empty!(&'static [!]);

mod visibility {
    /// This struct can only be seen to be inhabited in modules `b`, `c` or `d`, because otherwise
    /// the uninhabitedness of both `SecretlyUninhabited` structs is hidden.
    struct SometimesEmptyStruct {
        x: a::b::SecretlyUninhabited,
        y: c::AlsoSecretlyUninhabited,
    }

    /// This enum can only be seen to be inhabited in module `d`.
    enum SometimesEmptyEnum {
        X(c::AlsoSecretlyUninhabited),
        Y(c::d::VerySecretlyUninhabited),
    }

    mod a {
        use super::*;
        pub mod b {
            use super::*;
            pub struct SecretlyUninhabited {
                _priv: !,
            }
            assert_empty!(SometimesEmptyStruct);
        }

        assert_non_empty!(SometimesEmptyStruct);
        assert_non_empty!(SometimesEmptyEnum);
    }

    mod c {
        use super::*;
        pub struct AlsoSecretlyUninhabited {
            _priv: ::Struct1,
        }
        assert_empty!(SometimesEmptyStruct);
        assert_non_empty!(SometimesEmptyEnum);

        pub mod d {
            use super::*;
            pub struct VerySecretlyUninhabited {
                _priv: !,
            }
            assert_empty!(SometimesEmptyStruct);
            assert_empty!(SometimesEmptyEnum);
        }
    }

    assert_non_empty!(SometimesEmptyStruct);
    assert_non_empty!(SometimesEmptyEnum);
}

fn main() {}