Make possible to use procedural macros with scopes

Example usage:

fn main() {
    let x = {
        #[cfg(debug_assertions)]
        5
        
        #[cfg(not(debug_asserttions))]
        3
    };
    
    println!("{x}");
}

or

        let debug_utils = {
            #[cfg(validation)]
            Some({
                let debug_utils_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
                    .message_severity(
                        vk::DebugUtilsMessageSeverityFlagsEXT::ERROR
                            | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
                            | vk::DebugUtilsMessageSeverityFlagsEXT::INFO,
                    )
                    .message_type(
                        vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
                            | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
                            | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION,
                    );

                let debug_loader = ash::extensions::ext::DebugUtils::new(&entry, &instance);
                unsafe {
                    debug_loader
                        .create_debug_utils_messenger(&debug_utils_info, None)
                        .unwrap()
                }
            })

            #[cfg(not(validation))]
            None
        };

Currently it gives an error:

error: expected `;`, found `#`
 --> src/main.rs:4:10
  |
4 |         5
  |          ^ help: add `;` here
5 |         
6 |         #[cfg(not(debug_asserttions))]
  |         - unexpected token

error[E0308]: mismatched types
 --> src/main.rs:4:9
  |
4 |         5
  |         ^ expected `()`, found integer

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors
error: expected `;`, found `#`
  --> src\engine\backend\mod.rs:77:15
   |
77 |             })
   |               ^ help: add `;` here
78 |
79 |             #[cfg(not(validation))]
   |             - unexpected token

error: could not compile `vulkan_learning` due to previous error

Setting aside the potential issues that arise if the various attributes aren’t exhaustive, the problem with annotating expressions instead of statements is that they’re recursive: Many different expressions of different nesting levels start at the same point. For example, what does this evaluate to when p is true, 3 or 8?

#[cfg(p)] 3 #[cfg(not(p))] 4 + 5

Your examples can be made to compile today by lifting the annotation to cover the containing let statement. The resulting duplication seems like a small price to pay to avoid the ambiguity above; do you have any examples where lifting the annotation to an entire statement is a significant burden?

1 Like

You don't even need to lift the cfg to the let — you just need to cfg blocks instead of expressions, e.g.

fn main() {
    let x = {
        #[cfg(debug_assertions)] {
            5
        }
        
        #[cfg(not(debug_asserttions))] {
            3
        }
    };
    
    println!("{x}");
}
5 Likes
let x = if cfg!(debug_assertions) { 5 } else { 3 };
3 Likes

This is a runtime check. But I speak about compile-time.

There is no runtime check. The other branch is completly removed even without optimizations: Compiler Explorer

2 Likes

It's trivially removed, yes, but it still needs to typecheck, and thus can't use #[cfg]d functionality.

if cfg! is typically better if both arms do always compile (not the least because you always check both arms compile), but #[cfg] blocks are still sometimes required.

2 Likes