about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-01-16 17:00:44 +0100
committerGitHub <noreply@github.com>2025-01-16 17:00:44 +0100
commitfca148185e967ca166f78f63171973f8d540b339 (patch)
tree972ea7bdf00e943e4df4c38d0ac8d2d5d40a4d7d
parentd8a64098c9d0fb25699f657c6efff0bb418f7e18 (diff)
parentc89f0dc01d08cbc79a7bcb8a308585fb1a6544f1 (diff)
downloadrust-fca148185e967ca166f78f63171973f8d540b339.tar.gz
rust-fca148185e967ca166f78f63171973f8d540b339.zip
Rollup merge of #133720 - c410-f3r:cfg-match-foo-bar-baz, r=joshtriplett
[cfg_match] Adjust syntax

A year has passed since the creation of #115585 and the feature, as expected, is not moving forward. Let's change that.

This PR proposes changing the arm's syntax from  `cfg(SOME_CONDITION) => { ... }` to `SOME_CODITION => {}`.

```rust
match_cfg! {
   unix => {
        fn foo() { /* unix specific functionality */ }
    }
    target_pointer_width = "32" => {
        fn foo() { /* non-unix, 32-bit functionality */ }
    }
    _ => {
        fn foo() { /* fallback implementation */ }
    }
}
```

Why? Because after several manual migrations in https://github.com/rust-lang/rust/pull/116342 it became clear,  at least for me, that `cfg` prefixes are unnecessary, verbose and redundant.

Again, everything is just a proposal to move things forward. If the shown syntax isn't ideal, feel free to close this PR or suggest other alternatives.
-rw-r--r--compiler/rustc_data_structures/src/flock.rs25
-rw-r--r--compiler/rustc_data_structures/src/profiling.rs63
-rw-r--r--compiler/rustc_span/src/analyze_source_file.rs160
-rw-r--r--library/core/src/macros/mod.rs52
-rw-r--r--library/core/tests/lib.rs3
-rw-r--r--library/core/tests/macros.rs53
-rw-r--r--library/core/tests/macros_bootstrap.rs193
7 files changed, 528 insertions, 21 deletions
diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs
index e03962a54ec..292a33d5646 100644
--- a/compiler/rustc_data_structures/src/flock.rs
+++ b/compiler/rustc_data_structures/src/flock.rs
@@ -4,6 +4,7 @@
 //! green/native threading. This is just a bare-bones enough solution for
 //! librustdoc, it is not production quality at all.
 
+#[cfg(bootstrap)]
 cfg_match! {
     cfg(target_os = "linux") => {
         mod linux;
@@ -27,4 +28,28 @@ cfg_match! {
     }
 }
 
+#[cfg(not(bootstrap))]
+cfg_match! {
+    target_os = "linux" => {
+        mod linux;
+        use linux as imp;
+    }
+    target_os = "redox" => {
+        mod linux;
+        use linux as imp;
+    }
+    unix => {
+        mod unix;
+        use unix as imp;
+    }
+    windows => {
+        mod windows;
+        use self::windows as imp;
+    }
+    _ => {
+        mod unsupported;
+        use unsupported as imp;
+    }
+}
+
 pub use imp::Lock;
diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs
index 19050746c2f..18e98e6c39f 100644
--- a/compiler/rustc_data_structures/src/profiling.rs
+++ b/compiler/rustc_data_structures/src/profiling.rs
@@ -860,6 +860,7 @@ fn get_thread_id() -> u32 {
 }
 
 // Memory reporting
+#[cfg(bootstrap)]
 cfg_match! {
     cfg(windows) => {
         pub fn get_resident_set_size() -> Option<usize> {
@@ -921,5 +922,67 @@ cfg_match! {
     }
 }
 
+#[cfg(not(bootstrap))]
+cfg_match! {
+    windows => {
+        pub fn get_resident_set_size() -> Option<usize> {
+            use std::mem;
+
+            use windows::{
+                Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS},
+                Win32::System::Threading::GetCurrentProcess,
+            };
+
+            let mut pmc = PROCESS_MEMORY_COUNTERS::default();
+            let pmc_size = mem::size_of_val(&pmc);
+            unsafe {
+                K32GetProcessMemoryInfo(
+                    GetCurrentProcess(),
+                    &mut pmc,
+                    pmc_size as u32,
+                )
+            }
+            .ok()
+            .ok()?;
+
+            Some(pmc.WorkingSetSize)
+        }
+    }
+    target_os = "macos" => {
+        pub fn get_resident_set_size() -> Option<usize> {
+            use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO};
+            use std::mem;
+            const PROC_TASKINFO_SIZE: c_int = mem::size_of::<proc_taskinfo>() as c_int;
+
+            unsafe {
+                let mut info: proc_taskinfo = mem::zeroed();
+                let info_ptr = &mut info as *mut proc_taskinfo as *mut c_void;
+                let pid = getpid() as c_int;
+                let ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, info_ptr, PROC_TASKINFO_SIZE);
+                if ret == PROC_TASKINFO_SIZE {
+                    Some(info.pti_resident_size as usize)
+                } else {
+                    None
+                }
+            }
+        }
+    }
+    unix => {
+        pub fn get_resident_set_size() -> Option<usize> {
+            let field = 1;
+            let contents = fs::read("/proc/self/statm").ok()?;
+            let contents = String::from_utf8(contents).ok()?;
+            let s = contents.split_whitespace().nth(field)?;
+            let npages = s.parse::<usize>().ok()?;
+            Some(npages * 4096)
+        }
+    }
+    _ => {
+        pub fn get_resident_set_size() -> Option<usize> {
+            None
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs
index 28ce883daee..fba20566580 100644
--- a/compiler/rustc_span/src/analyze_source_file.rs
+++ b/compiler/rustc_span/src/analyze_source_file.rs
@@ -29,6 +29,7 @@ pub(crate) fn analyze_source_file(src: &str) -> (Vec<RelativeBytePos>, Vec<Multi
     (lines, multi_byte_chars)
 }
 
+#[cfg(bootstrap)]
 cfg_match! {
     cfg(any(target_arch = "x86", target_arch = "x86_64")) => {
         fn analyze_source_file_dispatch(
@@ -185,6 +186,165 @@ cfg_match! {
         }
     }
 }
+
+#[cfg(not(bootstrap))]
+cfg_match! {
+    any(target_arch = "x86", target_arch = "x86_64") => {
+        fn analyze_source_file_dispatch(
+            src: &str,
+            lines: &mut Vec<RelativeBytePos>,
+            multi_byte_chars: &mut Vec<MultiByteChar>,
+        ) {
+            if is_x86_feature_detected!("sse2") {
+                unsafe {
+                    analyze_source_file_sse2(src, lines, multi_byte_chars);
+                }
+            } else {
+                analyze_source_file_generic(
+                    src,
+                    src.len(),
+                    RelativeBytePos::from_u32(0),
+                    lines,
+                    multi_byte_chars,
+                );
+            }
+        }
+
+        /// Checks 16 byte chunks of text at a time. If the chunk contains
+        /// something other than printable ASCII characters and newlines, the
+        /// function falls back to the generic implementation. Otherwise it uses
+        /// SSE2 intrinsics to quickly find all newlines.
+        #[target_feature(enable = "sse2")]
+        unsafe fn analyze_source_file_sse2(
+            src: &str,
+            lines: &mut Vec<RelativeBytePos>,
+            multi_byte_chars: &mut Vec<MultiByteChar>,
+        ) {
+            #[cfg(target_arch = "x86")]
+            use std::arch::x86::*;
+            #[cfg(target_arch = "x86_64")]
+            use std::arch::x86_64::*;
+
+            const CHUNK_SIZE: usize = 16;
+
+            let src_bytes = src.as_bytes();
+
+            let chunk_count = src.len() / CHUNK_SIZE;
+
+            // This variable keeps track of where we should start decoding a
+            // chunk. If a multi-byte character spans across chunk boundaries,
+            // we need to skip that part in the next chunk because we already
+            // handled it.
+            let mut intra_chunk_offset = 0;
+
+            for chunk_index in 0..chunk_count {
+                let ptr = src_bytes.as_ptr() as *const __m128i;
+                // We don't know if the pointer is aligned to 16 bytes, so we
+                // use `loadu`, which supports unaligned loading.
+                let chunk = unsafe { _mm_loadu_si128(ptr.add(chunk_index)) };
+
+                // For character in the chunk, see if its byte value is < 0, which
+                // indicates that it's part of a UTF-8 char.
+                let multibyte_test = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)) };
+                // Create a bit mask from the comparison results.
+                let multibyte_mask = unsafe { _mm_movemask_epi8(multibyte_test) };
+
+                // If the bit mask is all zero, we only have ASCII chars here:
+                if multibyte_mask == 0 {
+                    assert!(intra_chunk_offset == 0);
+
+                    // Check if there are any control characters in the chunk. All
+                    // control characters that we can encounter at this point have a
+                    // byte value less than 32 or ...
+                    let control_char_test0 = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(32)) };
+                    let control_char_mask0 = unsafe { _mm_movemask_epi8(control_char_test0) };
+
+                    // ... it's the ASCII 'DEL' character with a value of 127.
+                    let control_char_test1 = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(127)) };
+                    let control_char_mask1 = unsafe { _mm_movemask_epi8(control_char_test1) };
+
+                    let control_char_mask = control_char_mask0 | control_char_mask1;
+
+                    if control_char_mask != 0 {
+                        // Check for newlines in the chunk
+                        let newlines_test = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)) };
+                        let newlines_mask = unsafe { _mm_movemask_epi8(newlines_test) };
+
+                        if control_char_mask == newlines_mask {
+                            // All control characters are newlines, record them
+                            let mut newlines_mask = 0xFFFF0000 | newlines_mask as u32;
+                            let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1);
+
+                            loop {
+                                let index = newlines_mask.trailing_zeros();
+
+                                if index >= CHUNK_SIZE as u32 {
+                                    // We have arrived at the end of the chunk.
+                                    break;
+                                }
+
+                                lines.push(RelativeBytePos(index) + output_offset);
+
+                                // Clear the bit, so we can find the next one.
+                                newlines_mask &= (!1) << index;
+                            }
+
+                            // We are done for this chunk. All control characters were
+                            // newlines and we took care of those.
+                            continue;
+                        } else {
+                            // Some of the control characters are not newlines,
+                            // fall through to the slow path below.
+                        }
+                    } else {
+                        // No control characters, nothing to record for this chunk
+                        continue;
+                    }
+                }
+
+                // The slow path.
+                // There are control chars in here, fallback to generic decoding.
+                let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset;
+                intra_chunk_offset = analyze_source_file_generic(
+                    &src[scan_start..],
+                    CHUNK_SIZE - intra_chunk_offset,
+                    RelativeBytePos::from_usize(scan_start),
+                    lines,
+                    multi_byte_chars,
+                );
+            }
+
+            // There might still be a tail left to analyze
+            let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset;
+            if tail_start < src.len() {
+                analyze_source_file_generic(
+                    &src[tail_start..],
+                    src.len() - tail_start,
+                    RelativeBytePos::from_usize(tail_start),
+                    lines,
+                    multi_byte_chars,
+                );
+            }
+        }
+    }
+    _ => {
+        // The target (or compiler version) does not support SSE2 ...
+        fn analyze_source_file_dispatch(
+            src: &str,
+            lines: &mut Vec<RelativeBytePos>,
+            multi_byte_chars: &mut Vec<MultiByteChar>,
+        ) {
+            analyze_source_file_generic(
+                src,
+                src.len(),
+                RelativeBytePos::from_u32(0),
+                lines,
+                multi_byte_chars,
+            );
+        }
+    }
+}
+
 // `scan_len` determines the number of bytes in `src` to scan. Note that the
 // function can read past `scan_len` if a multi-byte character start within the
 // range but extends past it. The overflow is returned by the function.
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 402b436d28e..5c04e5a40df 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -224,6 +224,7 @@ pub macro assert_matches {
 ///     }
 /// }
 /// ```
+#[cfg(bootstrap)]
 #[unstable(feature = "cfg_match", issue = "115585")]
 #[rustc_diagnostic_item = "cfg_match"]
 pub macro cfg_match {
@@ -284,6 +285,57 @@ pub macro cfg_match {
     }
 }
 
+/// A macro for defining `#[cfg]` match-like statements.
+///
+/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of
+/// `#[cfg]` cases, emitting the implementation which matches first.
+///
+/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
+/// without having to rewrite each clause multiple times.
+///
+/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when
+/// all previous declarations do not evaluate to true.
+///
+/// # Example
+///
+/// ```
+/// #![feature(cfg_match)]
+///
+/// cfg_match! {
+///     unix => {
+///         fn foo() { /* unix specific functionality */ }
+///     }
+///     target_pointer_width = "32" => {
+///         fn foo() { /* non-unix, 32-bit functionality */ }
+///     }
+///     _ => {
+///         fn foo() { /* fallback implementation */ }
+///     }
+/// }
+/// ```
+#[cfg(not(bootstrap))]
+#[unstable(feature = "cfg_match", issue = "115585")]
+#[rustc_diagnostic_item = "cfg_match"]
+pub macro cfg_match {
+    ({ $($tt:tt)* }) => {{
+        cfg_match! { $($tt)* }
+    }},
+    (_ => { $($output:tt)* }) => {
+        $($output)*
+    },
+    (
+        $cfg:meta => $output:tt
+        $($( $rest:tt )+)?
+    ) => {
+        #[cfg($cfg)]
+        cfg_match! { _ => $output }
+        $(
+            #[cfg(not($cfg))]
+            cfg_match! { $($rest)+ }
+        )?
+    },
+}
+
 /// Asserts that a boolean expression is `true` at runtime.
 ///
 /// This will invoke the [`panic!`] macro if the provided expression cannot be
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 18feee9fb25..a8980c5f30a 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -153,7 +153,10 @@ mod intrinsics;
 mod io;
 mod iter;
 mod lazy;
+#[cfg(not(bootstrap))]
 mod macros;
+#[cfg(bootstrap)]
+mod macros_bootstrap;
 mod manually_drop;
 mod mem;
 mod net;
diff --git a/library/core/tests/macros.rs b/library/core/tests/macros.rs
index fdb4ea29412..b30a40b7df2 100644
--- a/library/core/tests/macros.rs
+++ b/library/core/tests/macros.rs
@@ -10,7 +10,7 @@ struct Struct;
 
 impl Trait for Struct {
     cfg_match! {
-        cfg(feature = "blah") => {
+        feature = "blah" => {
             fn blah(&self) {
                 unimplemented!();
             }
@@ -47,21 +47,21 @@ fn matches_leading_pipe() {
 #[test]
 fn cfg_match_basic() {
     cfg_match! {
-        cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }}
+        target_pointer_width = "64" => { fn f0_() -> bool { true }}
     }
 
     cfg_match! {
-        cfg(unix) => { fn f1_() -> bool { true }}
-        cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }}
+        unix => { fn f1_() -> bool { true } }
+        any(target_os = "macos", target_os = "linux") => { fn f1_() -> bool { false }}
     }
 
     cfg_match! {
-        cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }}
-        cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }}
+        target_pointer_width = "32" => { fn f2_() -> bool { false } }
+        target_pointer_width = "64" => { fn f2_() -> bool { true } }
     }
 
     cfg_match! {
-        cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }}
+        target_pointer_width = "16" => { fn f3_() -> i32 { 1 } }
         _ => { fn f3_() -> i32 { 2 }}
     }
 
@@ -83,7 +83,7 @@ fn cfg_match_basic() {
 #[test]
 fn cfg_match_debug_assertions() {
     cfg_match! {
-        cfg(debug_assertions) => {
+        debug_assertions => {
             assert!(cfg!(debug_assertions));
             assert_eq!(4, 2+2);
         }
@@ -98,13 +98,13 @@ fn cfg_match_debug_assertions() {
 #[test]
 fn cfg_match_no_duplication_on_64() {
     cfg_match! {
-        cfg(windows) => {
+        windows => {
             fn foo() {}
         }
-        cfg(unix) => {
+        unix => {
             fn foo() {}
         }
-        cfg(target_pointer_width = "64") => {
+        target_pointer_width = "64" => {
             fn foo() {}
         }
     }
@@ -114,7 +114,7 @@ fn cfg_match_no_duplication_on_64() {
 #[test]
 fn cfg_match_options() {
     cfg_match! {
-        cfg(test) => {
+        test => {
             use core::option::Option as Option2;
             fn works1() -> Option2<u32> { Some(1) }
         }
@@ -122,26 +122,26 @@ fn cfg_match_options() {
     }
 
     cfg_match! {
-        cfg(feature = "foo") => { fn works2() -> bool { false } }
-        cfg(test) => { fn works2() -> bool { true } }
+        feature = "foo" => { fn works2() -> bool { false } }
+        test => { fn works2() -> bool { true } }
         _ => { fn works2() -> bool { false } }
     }
 
     cfg_match! {
-        cfg(feature = "foo") => { fn works3() -> bool { false } }
+        feature = "foo" => { fn works3() -> bool { false } }
         _ => { fn works3() -> bool { true } }
     }
 
     cfg_match! {
-        cfg(test) => {
+        test => {
             use core::option::Option as Option3;
             fn works4() -> Option3<u32> { Some(1) }
         }
     }
 
     cfg_match! {
-        cfg(feature = "foo") => { fn works5() -> bool { false } }
-        cfg(test) => { fn works5() -> bool { true } }
+        feature = "foo" => { fn works5() -> bool { false } }
+        test => { fn works5() -> bool { true } }
     }
 
     assert!(works1().is_some());
@@ -154,7 +154,7 @@ fn cfg_match_options() {
 #[test]
 fn cfg_match_two_functions() {
     cfg_match! {
-        cfg(target_pointer_width = "64") => {
+        target_pointer_width = "64" => {
             fn foo1() {}
             fn bar1() {}
         }
@@ -178,7 +178,7 @@ fn cfg_match_two_functions() {
 
 fn _accepts_expressions() -> i32 {
     cfg_match! {
-        cfg(unix) => { 1 }
+        unix => { 1 }
         _ => { 2 }
     }
 }
@@ -189,7 +189,18 @@ fn _allows_stmt_expr_attributes() {
     let one = 1;
     let two = 2;
     cfg_match! {
-        cfg(unix) => { one * two; }
+        unix => { one * two; }
         _ => { one + two; }
     }
 }
+
+fn _expression() {
+    let _ = cfg_match!({
+        windows => {
+            " XP"
+        }
+        _ => {
+            ""
+        }
+    });
+}
diff --git a/library/core/tests/macros_bootstrap.rs b/library/core/tests/macros_bootstrap.rs
new file mode 100644
index 00000000000..f10ef862c5d
--- /dev/null
+++ b/library/core/tests/macros_bootstrap.rs
@@ -0,0 +1,193 @@
+#![allow(unused_must_use)]
+
+#[allow(dead_code)]
+trait Trait {
+    fn blah(&self);
+}
+
+#[allow(dead_code)]
+struct Struct;
+
+impl Trait for Struct {
+    cfg_match! {
+        cfg(feature = "blah") => {
+            fn blah(&self) {
+                unimplemented!();
+            }
+        }
+        _ => {
+            fn blah(&self) {
+                unimplemented!();
+            }
+        }
+    }
+}
+
+#[test]
+fn assert_eq_trailing_comma() {
+    assert_eq!(1, 1,);
+}
+
+#[test]
+fn assert_escape() {
+    assert!(r#"☃\backslash"#.contains("\\"));
+}
+
+#[test]
+fn assert_ne_trailing_comma() {
+    assert_ne!(1, 2,);
+}
+
+#[rustfmt::skip]
+#[test]
+fn matches_leading_pipe() {
+    matches!(1, | 1 | 2 | 3);
+}
+
+#[test]
+fn cfg_match_basic() {
+    cfg_match! {
+        cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }}
+    }
+
+    cfg_match! {
+        cfg(unix) => { fn f1_() -> bool { true }}
+        cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }}
+    }
+
+    cfg_match! {
+        cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }}
+        cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }}
+    }
+
+    cfg_match! {
+        cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }}
+        _ => { fn f3_() -> i32 { 2 }}
+    }
+
+    #[cfg(target_pointer_width = "64")]
+    assert!(f0_());
+
+    #[cfg(unix)]
+    assert!(f1_());
+
+    #[cfg(target_pointer_width = "32")]
+    assert!(!f2_());
+    #[cfg(target_pointer_width = "64")]
+    assert!(f2_());
+
+    #[cfg(not(target_pointer_width = "16"))]
+    assert_eq!(f3_(), 2);
+}
+
+#[test]
+fn cfg_match_debug_assertions() {
+    cfg_match! {
+        cfg(debug_assertions) => {
+            assert!(cfg!(debug_assertions));
+            assert_eq!(4, 2+2);
+        }
+        _ => {
+            assert!(cfg!(not(debug_assertions)));
+            assert_eq!(10, 5+5);
+        }
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[test]
+fn cfg_match_no_duplication_on_64() {
+    cfg_match! {
+        cfg(windows) => {
+            fn foo() {}
+        }
+        cfg(unix) => {
+            fn foo() {}
+        }
+        cfg(target_pointer_width = "64") => {
+            fn foo() {}
+        }
+    }
+    foo();
+}
+
+#[test]
+fn cfg_match_options() {
+    cfg_match! {
+        cfg(test) => {
+            use core::option::Option as Option2;
+            fn works1() -> Option2<u32> { Some(1) }
+        }
+        _ => { fn works1() -> Option<u32> { None } }
+    }
+
+    cfg_match! {
+        cfg(feature = "foo") => { fn works2() -> bool { false } }
+        cfg(test) => { fn works2() -> bool { true } }
+        _ => { fn works2() -> bool { false } }
+    }
+
+    cfg_match! {
+        cfg(feature = "foo") => { fn works3() -> bool { false } }
+        _ => { fn works3() -> bool { true } }
+    }
+
+    cfg_match! {
+        cfg(test) => {
+            use core::option::Option as Option3;
+            fn works4() -> Option3<u32> { Some(1) }
+        }
+    }
+
+    cfg_match! {
+        cfg(feature = "foo") => { fn works5() -> bool { false } }
+        cfg(test) => { fn works5() -> bool { true } }
+    }
+
+    assert!(works1().is_some());
+    assert!(works2());
+    assert!(works3());
+    assert!(works4().is_some());
+    assert!(works5());
+}
+
+#[test]
+fn cfg_match_two_functions() {
+    cfg_match! {
+        cfg(target_pointer_width = "64") => {
+            fn foo1() {}
+            fn bar1() {}
+        }
+        _ => {
+            fn foo2() {}
+            fn bar2() {}
+        }
+    }
+
+    #[cfg(target_pointer_width = "64")]
+    {
+        foo1();
+        bar1();
+    }
+    #[cfg(not(target_pointer_width = "64"))]
+    {
+        foo2();
+        bar2();
+    }
+}
+
+fn _accepts_expressions() -> i32 {
+    cfg_match! {
+        cfg(unix) => { 1 }
+        _ => { 2 }
+    }
+}
+
+fn _allows_stmt_expr_attributes() {
+    let one = 1;
+    let two = 2;
+    cfg_match! {
+        cfg(unix) => { one * two; }
+        _ => { one + two; }
+    }
+}