diff --git a/CHANGELOG.md b/CHANGELOG.md index f24b1aa1a6..ec68f5130a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,11 +40,62 @@ Bottom level categories: ## Unreleased +## Major changes + +### The `diagnostic(…);` directive is now supported in WGSL + +Naga now parses `diagnostic(…);` directives according to the WGSL spec. This allows users to control certain lints, similar to Rust's `allow`, `warn`, and `deny` attributes. For example, in standard WGSL (but, notably, not Naga yet—see ) this snippet would emit a uniformity error: + +```wgsl +@group(0) @binding(0) var s : sampler; +@group(0) @binding(2) var tex : texture_2d; +@group(1) @binding(0) var ro_buffer : array; + +@fragment +fn main(@builtin(position) p : vec4f) -> @location(0) vec4f { + if ro_buffer[0] == 0 { + // Emits a derivative uniformity error during validation. + return textureSample(tex, s, vec2(0.,0.)); + } + + return vec4f(0.); +} +``` + +…but we can now silence it with the `off` severity level, like so: + +```wgsl +// Disable the diagnosic with this… +diagnostic(off, derivative_uniformity); + +@group(0) @binding(0) var s : sampler; +@group(0) @binding(2) var tex : texture_2d; +@group(1) @binding(0) var ro_buffer : array; + +@fragment +fn main(@builtin(position) p : vec4f) -> @location(0) vec4f { + if ro_buffer[0] == 0 { + // Look ma, no error! + return textureSample(tex, s, vec2(0.,0.)); + } + + return vec4f(0.); +} +``` + +There are some limitations to keep in mind with this new functionality: + +- We do not yet support `diagnostic(…)` rules in attribute position (i.e., `@diagnostic(…) fn my_func { … }`). This is being tracked in . We expect that rules in `fn` attribute position will be relaxed shortly (see ), but the prioritization for statement positions is unclear. If you are blocked by not being able to parse `diagnostic(…)` rules in statement positions, please let us know in that issue, so we can determine how to prioritize it! +- Standard WGSL specifies `error`, `warning`, `info`, and `off` severity levels. These are all technically usable now! A caveat, though: warning- and info-level are only emitted to `stderr` via the `log` façade, rather than being reported through a `Result::Err` in Naga or the `CompilationInfo` interface in `wgpu{,-core}`. This will require breaking changes in Naga to fix, and is being tracked by . +- Not all lints can be controlled with `diagnostic(…)` rules. In fact, only the `derivative_uniformity` triggering rule exists in the WGSL standard. That said, Naga contributors are excited to see how this level of control unlocks a new ecosystem of configurable diagnostics. +- Finally, `diagnostic(…)` rules are not yet emitted in WGSL output. This means that `wgsl-in` → `wgsl-out` is currently a lossy process. We felt that it was important to unblock users who needed `diagnostic(…)` rules (i.e., ) before we took significant effort to fix this (tracked in ). + +By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456), [#6148](https://github.com/gfx-rs/wgpu/pull/6148). + ### New Features #### Naga -- Parse `diagnostic(…)` directives, but don't implement any triggering rules yet. By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456). - Fix an issue where `naga` CLI would incorrectly skip the first positional argument when `--stdin-file-path` was specified. By @ErichDonGubler in [#6480](https://github.com/gfx-rs/wgpu/pull/6480). - Fix textureNumLevels in the GLSL backend. By @magcius in [#6483](https://github.com/gfx-rs/wgpu/pull/6483). - Implement `quantizeToF16()` for WGSL frontend, and WGSL, SPIR-V, HLSL, MSL, and GLSL backends. By @jamienicol in [#6519](https://github.com/gfx-rs/wgpu/pull/6519). diff --git a/naga/src/diagnostic_filter.rs b/naga/src/diagnostic_filter.rs index e2683b8fa9..602953ca43 100644 --- a/naga/src/diagnostic_filter.rs +++ b/naga/src/diagnostic_filter.rs @@ -1,9 +1,24 @@ //! [`DiagnosticFilter`]s and supporting functionality. +#[cfg(feature = "wgsl-in")] +use crate::Span; +use crate::{Arena, Handle}; +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; +#[cfg(feature = "wgsl-in")] +use indexmap::IndexMap; +#[cfg(feature = "deserialize")] +use serde::Deserialize; +#[cfg(feature = "serialize")] +use serde::Serialize; + /// A severity set on a [`DiagnosticFilter`]. /// /// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] pub enum Severity { Off, Info, @@ -33,7 +48,6 @@ impl Severity { /// Naga does not yet support diagnostic items at lesser severities than /// [`Severity::Error`]. When this is implemented, this method should be deleted, and the /// severity should be used directly for reporting diagnostics. - #[cfg(feature = "wgsl-in")] pub(crate) fn report_diag( self, err: E, @@ -57,6 +71,9 @@ impl Severity { /// /// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] pub enum FilterableTriggeringRule { DerivativeUniformity, } @@ -79,10 +96,13 @@ impl FilterableTriggeringRule { } } - #[cfg(feature = "wgsl-in")] - pub(crate) const fn tracking_issue_num(self) -> u16 { + /// The default severity associated with this triggering rule. + /// + /// See for a table of default + /// severities. + pub(crate) const fn default_severity(self) -> Severity { match self { - FilterableTriggeringRule::DerivativeUniformity => 5320, + FilterableTriggeringRule::DerivativeUniformity => Severity::Error, } } } @@ -91,7 +111,140 @@ impl FilterableTriggeringRule { /// /// #[derive(Clone, Debug)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] pub struct DiagnosticFilter { pub new_severity: Severity, pub triggering_rule: FilterableTriggeringRule, } + +/// A map of diagnostic filters to their severity and first occurrence's span. +/// +/// Intended for front ends' first step into storing parsed [`DiagnosticFilter`]s. +#[derive(Clone, Debug, Default)] +#[cfg(feature = "wgsl-in")] +pub(crate) struct DiagnosticFilterMap(IndexMap); + +#[cfg(feature = "wgsl-in")] +impl DiagnosticFilterMap { + pub(crate) fn new() -> Self { + Self::default() + } + + /// Add the given `diagnostic_filter` parsed at the given `span` to this map. + pub(crate) fn add( + &mut self, + diagnostic_filter: DiagnosticFilter, + span: Span, + ) -> Result<(), ConflictingDiagnosticRuleError> { + use indexmap::map::Entry; + + let &mut Self(ref mut diagnostic_filters) = self; + let DiagnosticFilter { + new_severity, + triggering_rule, + } = diagnostic_filter; + + match diagnostic_filters.entry(triggering_rule) { + Entry::Vacant(entry) => { + entry.insert((new_severity, span)); + } + Entry::Occupied(entry) => { + let &(first_severity, first_span) = entry.get(); + if first_severity != new_severity { + return Err(ConflictingDiagnosticRuleError { + triggering_rule, + triggering_rule_spans: [first_span, span], + }); + } + } + } + Ok(()) + } +} + +#[cfg(feature = "wgsl-in")] +impl IntoIterator for DiagnosticFilterMap { + type Item = (FilterableTriggeringRule, (Severity, Span)); + + type IntoIter = indexmap::map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + let Self(this) = self; + this.into_iter() + } +} + +/// An error returned by [`DiagnosticFilterMap::add`] when it encounters conflicting rules. +#[cfg(feature = "wgsl-in")] +#[derive(Clone, Debug)] +pub(crate) struct ConflictingDiagnosticRuleError { + pub triggering_rule: FilterableTriggeringRule, + pub triggering_rule_spans: [Span; 2], +} + +/// Represents a single parent-linking node in a tree of [`DiagnosticFilter`]s backed by a +/// [`crate::Arena`]. +/// +/// A single element of a _tree_ of diagnostic filter rules stored in +/// [`crate::Module::diagnostic_filters`]. When nodes are built by a front-end, module-applicable +/// filter rules are chained together in runs based on parse site. For instance, given the +/// following: +/// +/// - Module-applicable rules `a` and `b`. +/// - Rules `c` and `d`, applicable to an entry point called `c_and_d_func`. +/// - Rule `e`, applicable to an entry point called `e_func`. +/// +/// The tree would be represented as follows: +/// +/// ```text +/// a <- b +/// ^ +/// |- c <- d +/// | +/// \- e +/// ``` +/// +/// ...where: +/// +/// - `d` is the first leaf consulted by validation in `c_and_d_func`. +/// - `e` is the first leaf consulted by validation in `e_func`. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +pub struct DiagnosticFilterNode { + pub inner: DiagnosticFilter, + pub parent: Option>, +} + +impl DiagnosticFilterNode { + /// Finds the most specific filter rule applicable to `triggering_rule` from the chain of + /// diagnostic filter rules in `arena`, starting with `node`, and returns its severity. If none + /// is found, return the value of [`FilterableTriggeringRule::default_severity`]. + /// + /// When `triggering_rule` is not applicable to this node, its parent is consulted recursively. + pub(crate) fn search( + node: Option>, + arena: &Arena, + triggering_rule: FilterableTriggeringRule, + ) -> Severity { + let mut next = node; + while let Some(handle) = next { + let node = &arena[handle]; + let &Self { ref inner, parent } = node; + let &DiagnosticFilter { + triggering_rule: rule, + new_severity, + } = inner; + + if rule == triggering_rule { + return new_severity; + } + + next = parent; + } + triggering_rule.default_severity() + } +} diff --git a/naga/src/front/wgsl/error.rs b/naga/src/front/wgsl/error.rs index f5e6332085..c3f104b2b2 100644 --- a/naga/src/front/wgsl/error.rs +++ b/naga/src/front/wgsl/error.rs @@ -1,4 +1,4 @@ -use crate::diagnostic_filter::FilterableTriggeringRule; +use crate::diagnostic_filter::ConflictingDiagnosticRuleError; use crate::front::wgsl::parse::directive::enable_extension::{ EnableExtension, UnimplementedEnableExtension, }; @@ -295,10 +295,13 @@ pub(crate) enum Error<'a> { DiagnosticInvalidSeverity { severity_control_name_span: Span, }, - DiagnosticNotYetImplemented { - triggering_rule: FilterableTriggeringRule, - span: Span, - }, + DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError), +} + +impl<'a> From for Error<'a> { + fn from(value: ConflictingDiagnosticRuleError) -> Self { + Self::DiagnosticDuplicateTriggeringRule(value) + } } #[derive(Clone, Debug)] @@ -1017,24 +1020,29 @@ impl<'a> Error<'a> { ) .into()], }, - Error::DiagnosticNotYetImplemented { + Error::DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError { triggering_rule, - span, - } => ParseError { - message: format!( - "the `{}` diagnostic filter is not yet supported", - triggering_rule.to_ident() - ), - labels: vec![(span, "".into())], - notes: vec![format!( - concat!( - "Let Naga maintainers know that you ran into this at ", - ", ", - "so they can prioritize it!" + triggering_rule_spans, + }) => { + let [first_span, second_span] = triggering_rule_spans; + ParseError { + message: format!( + "found conflicting `diagnostic(…)` rule(s) for `{}`", + triggering_rule.to_ident() ), - triggering_rule.tracking_issue_num() - )], - }, + labels: vec![ + (first_span, "first rule".into()), + (second_span, "second rule".into()), + ], + notes: vec![concat!( + "multiple `diagnostic(…)` rules with the same rule name ", + "conflict unless the severity is the same; ", + "delete the rule you don't want, or ", + "ensure that all severities with the same rule name match" + ) + .into()], + } + } } } } diff --git a/naga/src/front/wgsl/lower/mod.rs b/naga/src/front/wgsl/lower/mod.rs index 78e81350b4..d413e883e4 100644 --- a/naga/src/front/wgsl/lower/mod.rs +++ b/naga/src/front/wgsl/lower/mod.rs @@ -1013,7 +1013,11 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { &mut self, tu: &'temp ast::TranslationUnit<'source>, ) -> Result> { - let mut module = crate::Module::default(); + let mut module = crate::Module { + diagnostic_filters: tu.diagnostic_filters.clone(), + diagnostic_filter_leaf: tu.diagnostic_filter_leaf, + ..Default::default() + }; let mut ctx = GlobalContext { ast_expressions: &tu.expressions, @@ -1244,7 +1248,7 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { .arguments .iter() .enumerate() - .map(|(i, arg)| { + .map(|(i, arg)| -> Result<_, Error<'_>> { let ty = self.resolve_ast_type(arg.ty, ctx)?; let expr = expressions .append(crate::Expression::FunctionArgument(i as u32), arg.name.span); @@ -1263,7 +1267,7 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { let result = f .result .as_ref() - .map(|res| { + .map(|res| -> Result<_, Error<'_>> { let ty = self.resolve_ast_type(res.ty, ctx)?; Ok(crate::FunctionResult { ty, diff --git a/naga/src/front/wgsl/parse/ast.rs b/naga/src/front/wgsl/parse/ast.rs index 81d8d90bb1..9385108934 100644 --- a/naga/src/front/wgsl/parse/ast.rs +++ b/naga/src/front/wgsl/parse/ast.rs @@ -1,3 +1,4 @@ +use crate::diagnostic_filter::DiagnosticFilterNode; use crate::front::wgsl::parse::directive::enable_extension::EnableExtensions; use crate::front::wgsl::parse::number::Number; use crate::front::wgsl::Scalar; @@ -26,6 +27,17 @@ pub struct TranslationUnit<'a> { /// These are referred to by `Handle>` values. /// User-defined types are referred to by name until lowering. pub types: Arena>, + + /// Arena for all diagnostic filter rules parsed in this module, including those in functions. + /// + /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in + /// validation. + pub diagnostic_filters: Arena, + /// The leaf of all `diagnostic(…)` directives in this module. + /// + /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in + /// validation. + pub diagnostic_filter_leaf: Option>, } #[derive(Debug, Clone, Copy)] diff --git a/naga/src/front/wgsl/parse/mod.rs b/naga/src/front/wgsl/parse/mod.rs index 4e674062b1..06c5014953 100644 --- a/naga/src/front/wgsl/parse/mod.rs +++ b/naga/src/front/wgsl/parse/mod.rs @@ -1,4 +1,6 @@ -use crate::diagnostic_filter::{self, DiagnosticFilter, FilterableTriggeringRule}; +use crate::diagnostic_filter::{ + self, DiagnosticFilter, DiagnosticFilterMap, DiagnosticFilterNode, FilterableTriggeringRule, +}; use crate::front::wgsl::error::{Error, ExpectedToken}; use crate::front::wgsl::parse::directive::enable_extension::{ EnableExtension, EnableExtensions, UnimplementedEnableExtension, @@ -1907,10 +1909,8 @@ impl Parser { let _ = lexer.next(); let mut body = ast::Block::default(); - let (condition, span) = lexer.capture_span(|lexer| { - let condition = self.general_expression(lexer, ctx)?; - Ok(condition) - })?; + let (condition, span) = + lexer.capture_span(|lexer| self.general_expression(lexer, ctx))?; let mut reject = ast::Block::default(); reject.stmts.push(ast::Statement { kind: ast::StatementKind::Break, @@ -1966,11 +1966,12 @@ impl Parser { let mut body = ast::Block::default(); if !lexer.skip(Token::Separator(';')) { - let (condition, span) = lexer.capture_span(|lexer| { - let condition = self.general_expression(lexer, ctx)?; - lexer.expect(Token::Separator(';'))?; - Ok(condition) - })?; + let (condition, span) = + lexer.capture_span(|lexer| -> Result<_, Error<'_>> { + let condition = self.general_expression(lexer, ctx)?; + lexer.expect(Token::Separator(';'))?; + Ok(condition) + })?; let mut reject = ast::Block::default(); reject.stmts.push(ast::Statement { kind: ast::StatementKind::Break, @@ -2523,6 +2524,7 @@ impl Parser { let mut lexer = Lexer::new(source); let mut tu = ast::TranslationUnit::default(); let mut enable_extensions = EnableExtensions::empty(); + let mut diagnostic_filters = DiagnosticFilterMap::new(); // Parse directives. while let Ok((ident, _directive_ident_span)) = lexer.peek_ident_with_span() { @@ -2532,12 +2534,8 @@ impl Parser { match kind { DirectiveKind::Diagnostic => { if let Some(diagnostic_filter) = self.diagnostic_filter(&mut lexer)? { - let triggering_rule = diagnostic_filter.triggering_rule; let span = self.peek_rule_span(&lexer); - Err(Error::DiagnosticNotYetImplemented { - triggering_rule, - span, - })?; + diagnostic_filters.add(diagnostic_filter, span)?; } lexer.expect(Token::Separator(';'))?; } @@ -2583,6 +2581,8 @@ impl Parser { lexer.enable_extensions = enable_extensions.clone(); tu.enable_extensions = enable_extensions; + tu.diagnostic_filter_leaf = + Self::write_diagnostic_filters(&mut tu.diagnostic_filters, diagnostic_filters, None); loop { match self.global_decl(&mut lexer, &mut tu) { @@ -2674,4 +2674,25 @@ impl Parser { Ok(filter) } + + pub(crate) fn write_diagnostic_filters( + arena: &mut Arena, + filters: DiagnosticFilterMap, + parent: Option>, + ) -> Option> { + filters + .into_iter() + .fold(parent, |parent, (triggering_rule, (new_severity, span))| { + Some(arena.append( + DiagnosticFilterNode { + inner: DiagnosticFilter { + new_severity, + triggering_rule, + }, + parent, + }, + span, + )) + }) + } } diff --git a/naga/src/lib.rs b/naga/src/lib.rs index 873e902f0d..280a9a9d84 100644 --- a/naga/src/lib.rs +++ b/naga/src/lib.rs @@ -269,6 +269,7 @@ pub use crate::arena::{Arena, Handle, Range, UniqueArena}; pub use crate::span::{SourceLocation, Span, SpanContext, WithSpan}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; +use diagnostic_filter::DiagnosticFilterNode; #[cfg(feature = "deserialize")] use serde::Deserialize; #[cfg(feature = "serialize")] @@ -2272,4 +2273,17 @@ pub struct Module { pub functions: Arena, /// Entry points. pub entry_points: Vec, + /// Arena for all diagnostic filter rules parsed in this module, including those in functions + /// and statements. + /// + /// This arena contains elements of a _tree_ of diagnostic filter rules. When nodes are built + /// by a front-end, they refer to a parent scope + pub diagnostic_filters: Arena, + /// The leaf of all diagnostic filter rules tree parsed from directives in this module. + /// + /// In WGSL, this corresponds to `diagnostic(…);` directives. + /// + /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in + /// validation. + pub diagnostic_filter_leaf: Option>, } diff --git a/naga/src/valid/analyzer.rs b/naga/src/valid/analyzer.rs index 6b4679632e..e169ed1d0a 100644 --- a/naga/src/valid/analyzer.rs +++ b/naga/src/valid/analyzer.rs @@ -6,6 +6,7 @@ //! - expression reference counts use super::{ExpressionError, FunctionError, ModuleInfo, ShaderStages, ValidationFlags}; +use crate::diagnostic_filter::{DiagnosticFilterNode, FilterableTriggeringRule}; use crate::span::{AddSpan as _, WithSpan}; use crate::{ arena::{Arena, Handle}, @@ -15,10 +16,6 @@ use std::ops; pub type NonUniformResult = Option>; -// Remove this once we update our uniformity analysis and -// add support for the `derivative_uniformity` diagnostic -const DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE: bool = true; - bitflags::bitflags! { /// Kinds of expressions that require uniform control flow. #[cfg_attr(feature = "serialize", derive(serde::Serialize))] @@ -26,8 +23,8 @@ bitflags::bitflags! { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct UniformityRequirements: u8 { const WORK_GROUP_BARRIER = 0x1; - const DERIVATIVE = if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE { 0 } else { 0x2 }; - const IMPLICIT_LEVEL = if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE { 0 } else { 0x4 }; + const DERIVATIVE = 0x2; + const IMPLICIT_LEVEL = 0x4; } } @@ -289,6 +286,13 @@ pub struct FunctionInfo { /// Indicates that the function is using dual source blending. pub dual_source_blending: bool, + + /// The leaf of all module-wide diagnostic filter rules tree parsed from directives in this + /// module. + /// + /// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in + /// validation. + diagnostic_filter_leaf: Option>, } impl FunctionInfo { @@ -826,6 +830,7 @@ impl FunctionInfo { other_functions: &[FunctionInfo], mut disruptor: Option, expression_arena: &Arena, + diagnostic_filter_arena: &Arena, ) -> Result> { use crate::Statement as S; @@ -842,8 +847,21 @@ impl FunctionInfo { && !req.is_empty() { if let Some(cause) = disruptor { - return Err(FunctionError::NonUniformControlFlow(req, expr, cause) - .with_span_handle(expr, expression_arena)); + let severity = DiagnosticFilterNode::search( + self.diagnostic_filter_leaf, + diagnostic_filter_arena, + FilterableTriggeringRule::DerivativeUniformity, + ); + severity.report_diag( + FunctionError::NonUniformControlFlow(req, expr, cause) + .with_span_handle(expr, expression_arena), + // TODO: Yes, this isn't contextualized with source, because + // the user is supposed to render what would normally be an + // error here. Once we actually support warning-level + // diagnostic items, then we won't need this non-compliant hack: + // + |e, level| log::log!(level, "{e}"), + )?; } } requirements |= req; @@ -901,9 +919,13 @@ impl FunctionInfo { exit: ExitFlags::empty(), } } - S::Block(ref b) => { - self.process_block(b, other_functions, disruptor, expression_arena)? - } + S::Block(ref b) => self.process_block( + b, + other_functions, + disruptor, + expression_arena, + diagnostic_filter_arena, + )?, S::If { condition, ref accept, @@ -917,12 +939,14 @@ impl FunctionInfo { other_functions, branch_disruptor, expression_arena, + diagnostic_filter_arena, )?; let reject_uniformity = self.process_block( reject, other_functions, branch_disruptor, expression_arena, + diagnostic_filter_arena, )?; accept_uniformity | reject_uniformity } @@ -941,6 +965,7 @@ impl FunctionInfo { other_functions, case_disruptor, expression_arena, + diagnostic_filter_arena, )?; case_disruptor = if case.fall_through { case_disruptor.or(case_uniformity.exit_disruptor()) @@ -956,14 +981,20 @@ impl FunctionInfo { ref continuing, break_if, } => { - let body_uniformity = - self.process_block(body, other_functions, disruptor, expression_arena)?; + let body_uniformity = self.process_block( + body, + other_functions, + disruptor, + expression_arena, + diagnostic_filter_arena, + )?; let continuing_disruptor = disruptor.or(body_uniformity.exit_disruptor()); let continuing_uniformity = self.process_block( continuing, other_functions, continuing_disruptor, expression_arena, + diagnostic_filter_arena, )?; if let Some(expr) = break_if { let _ = self.add_ref(expr); @@ -1117,6 +1148,7 @@ impl ModuleInfo { expressions: vec![ExpressionInfo::new(); fun.expressions.len()].into_boxed_slice(), sampling: crate::FastHashSet::default(), dual_source_blending: false, + diagnostic_filter_leaf: module.diagnostic_filter_leaf, }; let resolve_context = ResolveContext::with_locals(module, &fun.local_variables, &fun.arguments); @@ -1140,7 +1172,13 @@ impl ModuleInfo { } } - let uniformity = info.process_block(&fun.body, &self.functions, None, &fun.expressions)?; + let uniformity = info.process_block( + &fun.body, + &self.functions, + None, + &fun.expressions, + &module.diagnostic_filters, + )?; info.uniformity = uniformity.result; info.may_kill = uniformity.exit.contains(ExitFlags::MAY_KILL); @@ -1230,6 +1268,7 @@ fn uniform_control_flow() { expressions: vec![ExpressionInfo::new(); expressions.len()].into_boxed_slice(), sampling: crate::FastHashSet::default(), dual_source_blending: false, + diagnostic_filter_leaf: None, }; let resolve_context = ResolveContext { constants: &Arena::new(), @@ -1276,7 +1315,8 @@ fn uniform_control_flow() { &vec![stmt_emit1, stmt_if_uniform].into(), &[], None, - &expressions + &expressions, + &Arena::new(), ), Ok(FunctionUniformity { result: Uniformity { @@ -1303,26 +1343,59 @@ fn uniform_control_flow() { reject: crate::Block::new(), }; { + let block_info = info.process_block( + &vec![stmt_emit2.clone(), stmt_if_non_uniform.clone()].into(), + &[], + None, + &expressions, + &Arena::new(), + ); + assert_eq!( + block_info, + Err(FunctionError::NonUniformControlFlow( + UniformityRequirements::DERIVATIVE, + derivative_expr, + UniformityDisruptor::Expression(non_uniform_global_expr) + ) + .with_span()), + ); + assert_eq!(info[derivative_expr].ref_count, 1); + + // Test that the same thing passes when we disable the `derivative_uniformity` + let mut diagnostic_filters = Arena::new(); + let diagnostic_filter_leaf = diagnostic_filters.append( + DiagnosticFilterNode { + inner: crate::diagnostic_filter::DiagnosticFilter { + new_severity: crate::diagnostic_filter::Severity::Off, + triggering_rule: FilterableTriggeringRule::DerivativeUniformity, + }, + parent: None, + }, + crate::Span::default(), + ); + let mut info = FunctionInfo { + diagnostic_filter_leaf: Some(diagnostic_filter_leaf), + ..info.clone() + }; + let block_info = info.process_block( &vec![stmt_emit2, stmt_if_non_uniform].into(), &[], None, &expressions, + &diagnostic_filters, ); - if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE { - assert_eq!(info[derivative_expr].ref_count, 2); - } else { - assert_eq!( - block_info, - Err(FunctionError::NonUniformControlFlow( - UniformityRequirements::DERIVATIVE, - derivative_expr, - UniformityDisruptor::Expression(non_uniform_global_expr) - ) - .with_span()), - ); - assert_eq!(info[derivative_expr].ref_count, 1); - } + assert_eq!( + block_info, + Ok(FunctionUniformity { + result: Uniformity { + non_uniform_result: None, + requirements: UniformityRequirements::DERIVATIVE, + }, + exit: ExitFlags::empty() + }), + ); + assert_eq!(info[derivative_expr].ref_count, 2); } assert_eq!(info[non_uniform_global], GlobalUse::READ); @@ -1335,7 +1408,8 @@ fn uniform_control_flow() { &vec![stmt_emit3, stmt_return_non_uniform].into(), &[], Some(UniformityDisruptor::Return), - &expressions + &expressions, + &Arena::new(), ), Ok(FunctionUniformity { result: Uniformity { @@ -1362,7 +1436,8 @@ fn uniform_control_flow() { &vec![stmt_emit4, stmt_assign, stmt_kill, stmt_return_pointer].into(), &[], Some(UniformityDisruptor::Discard), - &expressions + &expressions, + &Arena::new(), ), Ok(FunctionUniformity { result: Uniformity { diff --git a/naga/src/valid/handles.rs b/naga/src/valid/handles.rs index f8be76d026..069261eb4a 100644 --- a/naga/src/valid/handles.rs +++ b/naga/src/valid/handles.rs @@ -2,6 +2,7 @@ use crate::{ arena::{BadHandle, BadRangeError}, + diagnostic_filter::DiagnosticFilterNode, Handle, }; @@ -39,6 +40,8 @@ impl super::Validator { ref types, ref special_types, ref global_expressions, + ref diagnostic_filters, + ref diagnostic_filter_leaf, } = module; // NOTE: Types being first is important. All other forms of validation depend on this. @@ -180,6 +183,14 @@ impl super::Validator { validate_type(ty)?; } + for (handle, _node) in diagnostic_filters.iter() { + let DiagnosticFilterNode { inner: _, parent } = diagnostic_filters[handle]; + handle.check_dep_opt(parent)?; + } + if let Some(handle) = *diagnostic_filter_leaf { + handle.check_valid_for(diagnostic_filters)?; + } + Ok(()) } diff --git a/naga/tests/in/diagnostic-filter.param.ron b/naga/tests/in/diagnostic-filter.param.ron new file mode 100644 index 0000000000..72873dd667 --- /dev/null +++ b/naga/tests/in/diagnostic-filter.param.ron @@ -0,0 +1,2 @@ +( +) diff --git a/naga/tests/in/diagnostic-filter.wgsl b/naga/tests/in/diagnostic-filter.wgsl new file mode 100644 index 0000000000..93a3942cf3 --- /dev/null +++ b/naga/tests/in/diagnostic-filter.wgsl @@ -0,0 +1 @@ +diagnostic(off, derivative_uniformity); diff --git a/naga/tests/out/analysis/access.info.ron b/naga/tests/out/analysis/access.info.ron index 1a45af466a..886f0bf9ed 100644 --- a/naga/tests/out/analysis/access.info.ron +++ b/naga/tests/out/analysis/access.info.ron @@ -1191,6 +1191,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2516,6 +2517,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2555,6 +2557,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2603,6 +2606,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2645,6 +2649,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2738,6 +2743,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2789,6 +2795,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2843,6 +2850,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2894,6 +2902,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -2948,6 +2957,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], entry_points: [ @@ -3623,6 +3633,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -4074,6 +4085,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -4194,6 +4206,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -4257,6 +4270,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], const_expression_types: [ diff --git a/naga/tests/out/analysis/collatz.info.ron b/naga/tests/out/analysis/collatz.info.ron index 94b879fcb8..6e7dd37bed 100644 --- a/naga/tests/out/analysis/collatz.info.ron +++ b/naga/tests/out/analysis/collatz.info.ron @@ -274,6 +274,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], entry_points: [ @@ -428,6 +429,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], const_expression_types: [], diff --git a/naga/tests/out/analysis/overrides.info.ron b/naga/tests/out/analysis/overrides.info.ron index 848d1780ed..0bb10336c8 100644 --- a/naga/tests/out/analysis/overrides.info.ron +++ b/naga/tests/out/analysis/overrides.info.ron @@ -163,6 +163,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], const_expression_types: [ diff --git a/naga/tests/out/analysis/shadow.info.ron b/naga/tests/out/analysis/shadow.info.ron index fb14beb1cb..e7a122dc7a 100644 --- a/naga/tests/out/analysis/shadow.info.ron +++ b/naga/tests/out/analysis/shadow.info.ron @@ -412,6 +412,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ( flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), @@ -1571,6 +1572,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], entry_points: [ @@ -1664,6 +1666,7 @@ ], sampling: [], dual_source_blending: false, + diagnostic_filter_leaf: None, ), ], const_expression_types: [ diff --git a/naga/tests/out/ir/access.compact.ron b/naga/tests/out/ir/access.compact.ron index 2e18f8b357..cae5d7d8e7 100644 --- a/naga/tests/out/ir/access.compact.ron +++ b/naga/tests/out/ir/access.compact.ron @@ -2476,4 +2476,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/access.ron b/naga/tests/out/ir/access.ron index 2e18f8b357..cae5d7d8e7 100644 --- a/naga/tests/out/ir/access.ron +++ b/naga/tests/out/ir/access.ron @@ -2476,4 +2476,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/atomic_i_increment.compact.ron b/naga/tests/out/ir/atomic_i_increment.compact.ron index 12a4692a3e..c7c33fcaaa 100644 --- a/naga/tests/out/ir/atomic_i_increment.compact.ron +++ b/naga/tests/out/ir/atomic_i_increment.compact.ron @@ -279,4 +279,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/atomic_i_increment.ron b/naga/tests/out/ir/atomic_i_increment.ron index 82fa975024..4fe2266a14 100644 --- a/naga/tests/out/ir/atomic_i_increment.ron +++ b/naga/tests/out/ir/atomic_i_increment.ron @@ -304,4 +304,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/collatz.compact.ron b/naga/tests/out/ir/collatz.compact.ron index 5999cf85a2..9091d269c7 100644 --- a/naga/tests/out/ir/collatz.compact.ron +++ b/naga/tests/out/ir/collatz.compact.ron @@ -330,4 +330,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/collatz.ron b/naga/tests/out/ir/collatz.ron index 5999cf85a2..9091d269c7 100644 --- a/naga/tests/out/ir/collatz.ron +++ b/naga/tests/out/ir/collatz.ron @@ -330,4 +330,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/const_assert.compact.ron b/naga/tests/out/ir/const_assert.compact.ron index 9dce67b5f9..8d4a729f9a 100644 --- a/naga/tests/out/ir/const_assert.compact.ron +++ b/naga/tests/out/ir/const_assert.compact.ron @@ -51,4 +51,6 @@ ), ], entry_points: [], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/const_assert.ron b/naga/tests/out/ir/const_assert.ron index 9dce67b5f9..8d4a729f9a 100644 --- a/naga/tests/out/ir/const_assert.ron +++ b/naga/tests/out/ir/const_assert.ron @@ -51,4 +51,6 @@ ), ], entry_points: [], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/diagnostic-filter.compact.ron b/naga/tests/out/ir/diagnostic-filter.compact.ron new file mode 100644 index 0000000000..e96fc1f4d4 --- /dev/null +++ b/naga/tests/out/ir/diagnostic-filter.compact.ron @@ -0,0 +1,24 @@ +( + types: [], + special_types: ( + ray_desc: None, + ray_intersection: None, + predeclared_types: {}, + ), + constants: [], + overrides: [], + global_variables: [], + global_expressions: [], + functions: [], + entry_points: [], + diagnostic_filters: [ + ( + inner: ( + new_severity: Off, + triggering_rule: DerivativeUniformity, + ), + parent: None, + ), + ], + diagnostic_filter_leaf: Some(0), +) \ No newline at end of file diff --git a/naga/tests/out/ir/diagnostic-filter.ron b/naga/tests/out/ir/diagnostic-filter.ron new file mode 100644 index 0000000000..e96fc1f4d4 --- /dev/null +++ b/naga/tests/out/ir/diagnostic-filter.ron @@ -0,0 +1,24 @@ +( + types: [], + special_types: ( + ray_desc: None, + ray_intersection: None, + predeclared_types: {}, + ), + constants: [], + overrides: [], + global_variables: [], + global_expressions: [], + functions: [], + entry_points: [], + diagnostic_filters: [ + ( + inner: ( + new_severity: Off, + triggering_rule: DerivativeUniformity, + ), + parent: None, + ), + ], + diagnostic_filter_leaf: Some(0), +) \ No newline at end of file diff --git a/naga/tests/out/ir/fetch_depth.compact.ron b/naga/tests/out/ir/fetch_depth.compact.ron index dc5ecf08ad..08c9296bb8 100644 --- a/naga/tests/out/ir/fetch_depth.compact.ron +++ b/naga/tests/out/ir/fetch_depth.compact.ron @@ -192,4 +192,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/fetch_depth.ron b/naga/tests/out/ir/fetch_depth.ron index 210a9ee26f..d48c09bfe1 100644 --- a/naga/tests/out/ir/fetch_depth.ron +++ b/naga/tests/out/ir/fetch_depth.ron @@ -262,4 +262,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/index-by-value.compact.ron b/naga/tests/out/ir/index-by-value.compact.ron index 9cc07b0776..461d5f6612 100644 --- a/naga/tests/out/ir/index-by-value.compact.ron +++ b/naga/tests/out/ir/index-by-value.compact.ron @@ -369,4 +369,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/index-by-value.ron b/naga/tests/out/ir/index-by-value.ron index 9cc07b0776..461d5f6612 100644 --- a/naga/tests/out/ir/index-by-value.ron +++ b/naga/tests/out/ir/index-by-value.ron @@ -369,4 +369,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/local-const.compact.ron b/naga/tests/out/ir/local-const.compact.ron index a9b9f32af8..4a0041f503 100644 --- a/naga/tests/out/ir/local-const.compact.ron +++ b/naga/tests/out/ir/local-const.compact.ron @@ -136,4 +136,6 @@ ), ], entry_points: [], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/local-const.ron b/naga/tests/out/ir/local-const.ron index a9b9f32af8..4a0041f503 100644 --- a/naga/tests/out/ir/local-const.ron +++ b/naga/tests/out/ir/local-const.ron @@ -136,4 +136,6 @@ ), ], entry_points: [], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.compact.ron b/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.compact.ron index 33846ef305..55184adaef 100644 --- a/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.compact.ron +++ b/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.compact.ron @@ -125,4 +125,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.ron b/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.ron index 33846ef305..55184adaef 100644 --- a/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.ron +++ b/naga/tests/out/ir/overrides-atomicCompareExchangeWeak.ron @@ -125,4 +125,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/overrides-ray-query.compact.ron b/naga/tests/out/ir/overrides-ray-query.compact.ron index 544f63ede9..a66c7ba73a 100644 --- a/naga/tests/out/ir/overrides-ray-query.compact.ron +++ b/naga/tests/out/ir/overrides-ray-query.compact.ron @@ -256,4 +256,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/overrides-ray-query.ron b/naga/tests/out/ir/overrides-ray-query.ron index 544f63ede9..a66c7ba73a 100644 --- a/naga/tests/out/ir/overrides-ray-query.ron +++ b/naga/tests/out/ir/overrides-ray-query.ron @@ -256,4 +256,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/overrides.compact.ron b/naga/tests/out/ir/overrides.compact.ron index e7921e7325..f1e3f411ee 100644 --- a/naga/tests/out/ir/overrides.compact.ron +++ b/naga/tests/out/ir/overrides.compact.ron @@ -196,4 +196,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/overrides.ron b/naga/tests/out/ir/overrides.ron index e7921e7325..f1e3f411ee 100644 --- a/naga/tests/out/ir/overrides.ron +++ b/naga/tests/out/ir/overrides.ron @@ -196,4 +196,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/shadow.compact.ron b/naga/tests/out/ir/shadow.compact.ron index a33b7d9968..65e78c1420 100644 --- a/naga/tests/out/ir/shadow.compact.ron +++ b/naga/tests/out/ir/shadow.compact.ron @@ -1026,4 +1026,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/shadow.ron b/naga/tests/out/ir/shadow.ron index 7662f586f1..88379a86c7 100644 --- a/naga/tests/out/ir/shadow.ron +++ b/naga/tests/out/ir/shadow.ron @@ -1304,4 +1304,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/spec-constants.compact.ron b/naga/tests/out/ir/spec-constants.compact.ron index e33bec6578..5e8a1c22cd 100644 --- a/naga/tests/out/ir/spec-constants.compact.ron +++ b/naga/tests/out/ir/spec-constants.compact.ron @@ -609,4 +609,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/out/ir/spec-constants.ron b/naga/tests/out/ir/spec-constants.ron index 8319c7bd3d..8a42262d20 100644 --- a/naga/tests/out/ir/spec-constants.ron +++ b/naga/tests/out/ir/spec-constants.ron @@ -715,4 +715,6 @@ ), ), ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, ) \ No newline at end of file diff --git a/naga/tests/snapshots.rs b/naga/tests/snapshots.rs index e132bc3d47..7a463946ef 100644 --- a/naga/tests/snapshots.rs +++ b/naga/tests/snapshots.rs @@ -941,6 +941,7 @@ fn convert_wgsl() { "6438-conflicting-idents", Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, ), + ("diagnostic-filter", Targets::IR), ]; for &(name, targets) in inputs.iter() {