//@ compile-flags: -Copt-level=1 //@ only-64bit #![crate_type = "lib"] #![feature(core_intrinsics)] // Check each of the 3 cases for `codegen_get_discr`. // FIXME: once our min-bar LLVM has `range` attributes, update the various // tests here to no longer have the `range`s and `nsw`s as optional. // Case 0: One tagged variant. pub enum Enum0 { A(bool), B, } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match0(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[IS_B:.+]] = icmp eq i8 %0, 2 // CHECK-NEXT: %[[TRUNC:.+]] = and i8 %0, 1 // CHECK-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %[[TRUNC]] // CHECK-NEXT: ret i8 %[[R]] #[no_mangle] pub fn match0(e: Enum0) -> u8 { use Enum0::*; match e { A(b) => b as u8, B => 13, } } // Case 1: Niche values are on a boundary for `range`. pub enum Enum1 { A(bool), B, C, } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match1(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, -2 // CHECK-NEXT: %[[REL_VAR_WIDE:.+]] = zext i8 %[[REL_VAR]] to i64 // CHECK-NEXT: %[[IS_NICHE:.+]] = icmp{{( samesign)?}} ugt i8 %0, 1 // CHECK-NEXT: %[[NICHE_DISCR:.+]] = add nuw nsw i64 %[[REL_VAR_WIDE]], 1 // CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i64 %[[NICHE_DISCR]], i64 0 // CHECK-NEXT: switch i64 %[[DISCR]] #[no_mangle] pub fn match1(e: Enum1) -> u8 { use Enum1::*; match e { A(b) => b as u8, B => 13, C => 100, } } // Case 2: Special cases don't apply. #[rustfmt::skip] pub enum X { _2=2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, _101, _102, _103, _104, _105, _106, _107, _108, _109, _110, _111, _112, _113, _114, _115, _116, _117, _118, _119, _120, _121, _122, _123, _124, _125, _126, _127, _128, _129, _130, _131, _132, _133, _134, _135, _136, _137, _138, _139, _140, _141, _142, _143, _144, _145, _146, _147, _148, _149, _150, _151, _152, _153, _154, _155, _156, _157, _158, _159, _160, _161, _162, _163, _164, _165, _166, _167, _168, _169, _170, _171, _172, _173, _174, _175, _176, _177, _178, _179, _180, _181, _182, _183, _184, _185, _186, _187, _188, _189, _190, _191, _192, _193, _194, _195, _196, _197, _198, _199, _200, _201, _202, _203, _204, _205, _206, _207, _208, _209, _210, _211, _212, _213, _214, _215, _216, _217, _218, _219, _220, _221, _222, _223, _224, _225, _226, _227, _228, _229, _230, _231, _232, _233, _234, _235, _236, _237, _238, _239, _240, _241, _242, _243, _244, _245, _246, _247, _248, _249, _250, _251, _252, _253, } pub enum Enum2 { A(X), B, C, D, E, } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, -?[0-9]+\))?}} i8 @match2(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[REL_VAR:.+]] = add i8 %0, 2 // CHECK-NEXT: %[[REL_VAR_WIDE:.+]] = zext i8 %[[REL_VAR]] to i64 // CHECK-NEXT: %[[IS_NICHE:.+]] = icmp ult i8 %[[REL_VAR]], 4 // CHECK-NEXT: %[[NICHE_DISCR:.+]] = add nuw nsw i64 %[[REL_VAR_WIDE]], 1 // CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i64 %[[NICHE_DISCR]], i64 0 // CHECK-NEXT: switch i64 %[[DISCR]] #[no_mangle] pub fn match2(e: Enum2) -> u8 { use Enum2::*; match e { A(b) => b as u8, B => 13, C => 100, D => 200, E => 250, } } // And make sure it works even if the niched scalar is a pointer. // (For example, that we don't try to `sub` on pointers.) // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i16 -?[0-9]+, -?[0-9]+\))?}} i16 @match3(ptr{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[IS_NULL:.+]] = icmp eq ptr %0, null // CHECK-NEXT: br i1 %[[IS_NULL]] #[no_mangle] pub fn match3(e: Option<&u8>) -> i16 { match e { Some(r) => *r as _, None => -1, } } // If the untagged variant is in the middle, there's an impossible value that's // not reflected in the `range` parameter attribute, so we assume it away. #[derive(PartialEq)] pub enum MiddleNiche { A, // tag 2 B, // tag 3 C(bool), // untagged D, // tag 5 E, // tag 6 } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 -?[0-9]+, -?[0-9]+\))?}} i8 @match4(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %0, 4 // CHECK-NEXT: call void @llvm.assume(i1 %[[NOT_IMPOSSIBLE]]) // CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, -2 // CHECK-NEXT: %[[NOT_NICHE:.+]] = icmp{{( samesign)?}} ult i8 %0, 2 // CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[NOT_NICHE]], i8 2, i8 %[[REL_VAR]] // CHECK-NEXT: switch i8 %[[DISCR]] #[no_mangle] pub fn match4(e: MiddleNiche) -> u8 { use MiddleNiche::*; match e { A => 13, B => 100, C(b) => b as u8, D => 200, E => 250, } } // CHECK-LABEL: define{{.+}}i1 @match4_is_c(i8{{.+}}%e) // CHECK-NEXT: start // CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %e, 4 // CHECK-NEXT: call void @llvm.assume(i1 %[[NOT_IMPOSSIBLE]]) // CHECK-NEXT: %[[IS_C:.+]] = icmp{{( samesign)?}} ult i8 %e, 2 // CHECK-NEXT: ret i1 %[[IS_C]] #[no_mangle] pub fn match4_is_c(e: MiddleNiche) -> bool { // Before #139098, this couldn't optimize out the `select` because it looked // like it was possible for a `2` to be produced on both sides. std::intrinsics::discriminant_value(&e) == 2 } // You have to do something pretty obnoxious to get a variant index that doesn't // fit in the tag size, but it's possible pub enum Never {} pub enum HugeVariantIndex { V000(Never), V001(Never), V002(Never), V003(Never), V004(Never), V005(Never), V006(Never), V007(Never), V008(Never), V009(Never), V010(Never), V011(Never), V012(Never), V013(Never), V014(Never), V015(Never), V016(Never), V017(Never), V018(Never), V019(Never), V020(Never), V021(Never), V022(Never), V023(Never), V024(Never), V025(Never), V026(Never), V027(Never), V028(Never), V029(Never), V030(Never), V031(Never), V032(Never), V033(Never), V034(Never), V035(Never), V036(Never), V037(Never), V038(Never), V039(Never), V040(Never), V041(Never), V042(Never), V043(Never), V044(Never), V045(Never), V046(Never), V047(Never), V048(Never), V049(Never), V050(Never), V051(Never), V052(Never), V053(Never), V054(Never), V055(Never), V056(Never), V057(Never), V058(Never), V059(Never), V060(Never), V061(Never), V062(Never), V063(Never), V064(Never), V065(Never), V066(Never), V067(Never), V068(Never), V069(Never), V070(Never), V071(Never), V072(Never), V073(Never), V074(Never), V075(Never), V076(Never), V077(Never), V078(Never), V079(Never), V080(Never), V081(Never), V082(Never), V083(Never), V084(Never), V085(Never), V086(Never), V087(Never), V088(Never), V089(Never), V090(Never), V091(Never), V092(Never), V093(Never), V094(Never), V095(Never), V096(Never), V097(Never), V098(Never), V099(Never), V100(Never), V101(Never), V102(Never), V103(Never), V104(Never), V105(Never), V106(Never), V107(Never), V108(Never), V109(Never), V110(Never), V111(Never), V112(Never), V113(Never), V114(Never), V115(Never), V116(Never), V117(Never), V118(Never), V119(Never), V120(Never), V121(Never), V122(Never), V123(Never), V124(Never), V125(Never), V126(Never), V127(Never), V128(Never), V129(Never), V130(Never), V131(Never), V132(Never), V133(Never), V134(Never), V135(Never), V136(Never), V137(Never), V138(Never), V139(Never), V140(Never), V141(Never), V142(Never), V143(Never), V144(Never), V145(Never), V146(Never), V147(Never), V148(Never), V149(Never), V150(Never), V151(Never), V152(Never), V153(Never), V154(Never), V155(Never), V156(Never), V157(Never), V158(Never), V159(Never), V160(Never), V161(Never), V162(Never), V163(Never), V164(Never), V165(Never), V166(Never), V167(Never), V168(Never), V169(Never), V170(Never), V171(Never), V172(Never), V173(Never), V174(Never), V175(Never), V176(Never), V177(Never), V178(Never), V179(Never), V180(Never), V181(Never), V182(Never), V183(Never), V184(Never), V185(Never), V186(Never), V187(Never), V188(Never), V189(Never), V190(Never), V191(Never), V192(Never), V193(Never), V194(Never), V195(Never), V196(Never), V197(Never), V198(Never), V199(Never), V200(Never), V201(Never), V202(Never), V203(Never), V204(Never), V205(Never), V206(Never), V207(Never), V208(Never), V209(Never), V210(Never), V211(Never), V212(Never), V213(Never), V214(Never), V215(Never), V216(Never), V217(Never), V218(Never), V219(Never), V220(Never), V221(Never), V222(Never), V223(Never), V224(Never), V225(Never), V226(Never), V227(Never), V228(Never), V229(Never), V230(Never), V231(Never), V232(Never), V233(Never), V234(Never), V235(Never), V236(Never), V237(Never), V238(Never), V239(Never), V240(Never), V241(Never), V242(Never), V243(Never), V244(Never), V245(Never), V246(Never), V247(Never), V248(Never), V249(Never), V250(Never), V251(Never), V252(Never), V253(Never), V254(Never), V255(Never), V256(Never), Possible257, // tag 2 Bool258(bool), // untagged Possible259, // tag 4 } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match5(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %0, 3 // CHECK-NEXT: call void @llvm.assume(i1 %[[NOT_IMPOSSIBLE]]) // CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, -2 // CHECK-NEXT: %[[REL_VAR_WIDE:.+]] = zext i8 %[[REL_VAR]] to i64 // CHECK-NEXT: %[[IS_NICHE:.+]] = icmp{{( samesign)?}} ugt i8 %0, 1 // CHECK-NEXT: %[[NICHE_DISCR:.+]] = add nuw nsw i64 %[[REL_VAR_WIDE]], 257 // CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i64 %[[NICHE_DISCR]], i64 258 // CHECK-NEXT: switch i64 %[[DISCR]], // CHECK-NEXT: i64 257, // CHECK-NEXT: i64 258, // CHECK-NEXT: i64 259, #[no_mangle] pub fn match5(e: HugeVariantIndex) -> u8 { use HugeVariantIndex::*; match e { Possible257 => 13, Bool258(b) => b as u8, Possible259 => 100, } } // Make an enum where the niche tags wrap both as signed and as unsigned, to hit // the most-fallback case where there's just nothing smart to do. pub enum E10Through65 { D10 = 10, D11 = 11, D12 = 12, D13 = 13, D14 = 14, D15 = 15, D16 = 16, D17 = 17, D18 = 18, D19 = 19, D20 = 20, D21 = 21, D22 = 22, D23 = 23, D24 = 24, D25 = 25, D26 = 26, D27 = 27, D28 = 28, D29 = 29, D30 = 30, D31 = 31, D32 = 32, D33 = 33, D34 = 34, D35 = 35, D36 = 36, D37 = 37, D38 = 38, D39 = 39, D40 = 40, D41 = 41, D42 = 42, D43 = 43, D44 = 44, D45 = 45, D46 = 46, D47 = 47, D48 = 48, D49 = 49, D50 = 50, D51 = 51, D52 = 52, D53 = 53, D54 = 54, D55 = 55, D56 = 56, D57 = 57, D58 = 58, D59 = 59, D60 = 60, D61 = 61, D62 = 62, D63 = 63, D64 = 64, D65 = 65, } pub enum Tricky { Untagged(E10Through65), V001, V002, V003, V004, V005, V006, V007, V008, V009, V010, V011, V012, V013, V014, V015, V016, V017, V018, V019, V020, V021, V022, V023, V024, V025, V026, V027, V028, V029, V030, V031, V032, V033, V034, V035, V036, V037, V038, V039, V040, V041, V042, V043, V044, V045, V046, V047, V048, V049, V050, V051, V052, V053, V054, V055, V056, V057, V058, V059, V060, V061, V062, V063, V064, V065, V066, V067, V068, V069, V070, V071, V072, V073, V074, V075, V076, V077, V078, V079, V080, V081, V082, V083, V084, V085, V086, V087, V088, V089, V090, V091, V092, V093, V094, V095, V096, V097, V098, V099, V100, V101, V102, V103, V104, V105, V106, V107, V108, V109, V110, V111, V112, V113, V114, V115, V116, V117, V118, V119, V120, V121, V122, V123, V124, V125, V126, V127, V128, V129, V130, V131, V132, V133, V134, V135, V136, V137, V138, V139, V140, V141, V142, V143, V144, V145, V146, V147, V148, V149, V150, V151, V152, V153, V154, V155, V156, V157, V158, V159, V160, V161, V162, V163, V164, V165, V166, V167, V168, V169, V170, V171, V172, V173, V174, V175, V176, V177, V178, V179, V180, V181, V182, V183, V184, V185, V186, V187, V188, V189, V190, V191, V192, V193, V194, V195, V196, V197, V198, V199, V200, } const _: () = assert!(std::intrinsics::discriminant_value(&Tricky::V100) == 100); // CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @discriminant6(i8 noundef{{( zeroext)?}} %e) // CHECK-NEXT: start: // CHECK-NEXT: %[[REL_VAR:.+]] = add i8 %e, -66 // CHECK-NEXT: %[[IS_NICHE:.+]] = icmp ult i8 %[[REL_VAR]], -56 // CHECK-NEXT: %[[TAGGED_DISCR:.+]] = add i8 %e, -65 // CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i8 %[[TAGGED_DISCR]], i8 0 // CHECK-NEXT: ret i8 %[[DISCR]] #[no_mangle] pub fn discriminant6(e: Tricky) -> u8 { std::intrinsics::discriminant_value(&e) as _ } // Case from , // where sign-extension is important. pub enum OpenResult { Ok(()), Err(()), TransportErr(TransportErr), } #[repr(i32)] pub enum TransportErr { UnknownMethod = -2, } #[no_mangle] pub fn match7(result: OpenResult) -> u8 { // CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match7(i32{{.+}}%result) // CHECK-NEXT: start: // CHECK-NEXT: %[[NOT_OK:.+]] = icmp ne i32 %result, -1 // CHECK-NEXT: %[[RET:.+]] = zext i1 %[[NOT_OK]] to i8 // CHECK-NEXT: ret i8 %[[RET]] match result { OpenResult::Ok(()) => 0, _ => 1, } }