WESL Logo

Conditional Translation

Overview

[!WARNING] This section is non-normative

Conditional translation is a mechanism to modify the output source code based on parameters passed to the WESL translator. This specificaton extends the attribute syntax with a new @if attribute. This attribute indicates that the syntax node it decorates can be removed by the WESL translator based on feature flags.

[!NOTE] This implementation is similar to the #[cfg(feature = "")] syntax in rust.

Usage Example

// global variables and bindings...
@if(textured)
@group(0) @binding(0) var my_texture: texture_2d<f32>;

// structs declarations and struct members...
struct Ray {
  position: vec4f,
  direction: vec4f,
  @if(debug_mode && raytracing_enabled)
  ray_steps: u32,
}

// function declarations, parameters and statements...
fn main() -> vec4 {
  @if(legacy_implementation || (is_web_version && xyz_not_supported))
  let result = legacy_impl();
  @if(!legacy_implementation && !(is_web_version && xyz_not_supported))
  let result = modern_impl();
}

Quirky examples

// attribute order does not matter.
@compute @if(feature) fn main() { }
// ... is equivalent to 
@if(feature) @compute fn main() { }

// feature names live in their own namespace, i.e. they cannot shadow, or be shadowed by declarations.
const feature1 = 10;
@if(feature1) fn main() -> u32 { // 'feature1' in @if does not refer to the const-declaration.
    return feature1*2;           // 'feature1' in return statement does not refer to the feature flag.
}

Definitions

Location of Translate-time attributes

A translate-time attribute can appear before the following syntax nodes:

[!NOTE] Translate-time attributes are not allowed in places where removal of the syntax node would lead to syntactically incorrect code. The current set of translate-time attribute locations guarantees that the code is syntactically correct after specialization. This is why translate-time attributes are not allowed before expressions.

Update to the WGSL grammar

The WGSL grammar allows attributes in several locations where translate-time attribute are not allowed (1). Conversely, the WGSL grammar does not allow attributes in several locations where translate-time attribute are allowed (2).

Refer to the updated grammar appendix for the list of updated grammar non-terminals.

  1. A translate-time attribute CANNOT decorate the following syntax nodes, even if the WGSL grammar allows attributes before these syntax nodes:

    • function return types
    • the body (part surrounded by curly braces) of:
      • function declarations
      • switch statements
      • switch clauses
      • loop statements
      • for statements
      • while statements
      • if/else statements
      • continuing statements
  2. The grammar is extended to allow translate-time attributes before the following syntax nodes:

    • const value declarations
    • variable declarations
    • directives
    • struct declarations
    • switch clauses
    • break statements
    • break-if statements
    • continue statements
    • continuing statements
    • return statements
    • discard statements
    • function call statements
    • const assertion statements

@if attribute

The @if translate-time attribute is introduced. It takes a single parameter. It marks the decorated node for removal if the parameter evaluates to false.

A syntax node may at most have a single @if attributes. This keeps the way open for a @else attribute introduction in the future. Checking for multiple features is done with an &&

@if(feature1 && feature2)   const decl: u32 = 0;

[!NOTE] See the possible future extensions for the attributes @elif and @else. They may be introduced in the specification in a future version if deemed useful.

Example

@if(feature_1 && (!feature_2 || feature_3))
fn f() { ... }
@if(!feature_1)                               // corresponds to @elif(!feature_1)
fn f() { ... }
@if(feature_1 && !(!feature_2 || feature_3))  // corresponds to @else
fn f() { ... }

Execution of the conditional translation phase

  1. The WESL translator is invoked with the list of features to enable or disable.

  2. The source file is parsed.

  3. The translate-time features in translate-time expressions are resolved:

    • If the feature is enabled, the identifier is replaced with true.
    • If the feature is disabled, the identifier is replaced with false.
  4. Translate-time attributes are evaluated:

    • If the decorated syntax node is marked for removal: it is eliminated from the source code along with the attribute.
    • Otherwise, only the attribute is eliminated from the source code.
  5. The updated source code is passed to the next translation phase. (e.g. import resolution)

Incremental translation

In case some features can only be resolved at runtime, a WESL translator can optionally support feature specialization in multiple passes:

If the WESL translator does not support incremental translation, it is an link-time error if any used Translate-time features was not provided to the linker.

[!NOTE] It is not an error to provide unused feature flags to the linker. However, an implementation may choose to display a warning in that case.

Possible future extensions

[!WARNING] This section is non-normative

Appendix: Updated grammar

The following non-terminals are added or modified:

diagnostic_directive :
 attribute * 'diagnostic' diagnostic_control ';'

enable_directive :
 attribute * 'enable' enable_extension_list ';'

requires_directive :
 attribute * 'requires' software_extension_list ';'

struct_decl :
 attribute * 'struct' ident struct_body_decl
 
type_alias_decl :
 attribute * 'alias' ident '=' type_specifier

variable_or_value_statement :
  variable_decl
| variable_decl '=' expression
| attribute * 'let' optionally_typed_ident '=' expression
| attribute * 'const' optionally_typed_ident '=' expression

variable_decl :
 attribute * 'var' _disambiguate_template template_list ? optionally_typed_ident
 
global_variable_decl :
 variable_decl ( '=' expression ) ?

global_value_decl :
  attribute * 'const' optionally_typed_ident '=' expression
| attribute * 'override' optionally_typed_ident ( '=' expression ) ?

case_clause :
 attribute * 'case' case_selectors ':' ? compound_statement

default_alone_clause :
 attribute * 'default' ':' ? compound_statement

break_statement :
 attribute * 'break'

break_if_statement :
 attribute * 'break' 'if' expression ';'

continue_statement :
 attribute * 'continue'
 
continuing_statement :
 attribute * 'continuing' continuing_compound_statement

return_statement :
 attribute * 'return' expression ?

discard_statement:
 attribute * 'discard'

func_call_statement :
 attribute * call_phrase

const_assert_statement :
 attribute * 'const_assert' expression

statement :
  ';'
| return_statement ';'
| if_statement
| switch_statement
| loop_statement
| for_statement
| while_statement
| func_call_statement ';'
| variable_or_value_statement ';'
| break_statement ';'
| continue_statement ';'
| discard_statement ';'
| variable_updating_statement ';'
| compound_statement
| const_assert_statement ';'

attribute :
  '@' ident_pattern_token argument_expression_list ?
| align_attr
| binding_attr
| blend_src_attr
| builtin_attr
| const_attr
| diagnostic_attr
| group_attr
| id_attr
| interpolate_attr
| invariant_attr
| location_attr
| must_use_attr
| size_attr
| workgroup_size_attr
| vertex_attr
| fragment_attr
| compute_attr
| if_attr

if_attr:
 '@' 'if' '(' expression ',' ? ')'