refactor(wgsl-in): track diagnostic directives in func. analysis

This commit is contained in:
Erich Gubler 2024-08-23 13:44:48 -04:00
parent 290ea1553b
commit b81fcb4134
40 changed files with 303 additions and 12 deletions

View File

@ -1,15 +1,24 @@
//! [`DiagnosticFilter`]s and supporting functionality.
use crate::Handle;
#[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`].
///
/// <https://www.w3.org/TR/WGSL/#diagnostic-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 Severity {
Off,
Info,
@ -63,6 +72,9 @@ impl Severity {
///
/// <https://www.w3.org/TR/WGSL/#filterable-triggering-rules>
#[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,
}
@ -84,12 +96,26 @@ impl FilterableTriggeringRule {
Self::DerivativeUniformity => Self::DERIVATIVE_UNIFORMITY,
}
}
/// The default severity associated with this triggering rule.
///
/// See <https://www.w3.org/TR/WGSL/#filterable-triggering-rules> for a table of default
/// severities.
#[allow(dead_code)]
pub(crate) const fn default_severity(self) -> Severity {
match self {
FilterableTriggeringRule::DerivativeUniformity => Severity::Error,
}
}
}
/// A filter that modifies how diagnostics are emitted for shaders.
///
/// <https://www.w3.org/TR/WGSL/#diagnostic-filter>
#[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,
@ -140,6 +166,18 @@ impl DiagnosticFilterMap {
}
}
#[cfg(feature = "wgsl-in")]
impl IntoIterator for DiagnosticFilterMap {
type Item = (FilterableTriggeringRule, (Severity, Span));
type IntoIter = indexmap::map::IntoIter<FilterableTriggeringRule, (Severity, Span)>;
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)]
@ -175,7 +213,41 @@ pub(crate) struct ConflictingDiagnosticRuleError {
/// - `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<Handle<DiagnosticFilterNode>>,
}
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.
#[allow(dead_code)]
pub(crate) fn search(
node: Option<Handle<Self>>,
arena: &Arena<Self>,
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()
}
}

View File

@ -1013,7 +1013,11 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
&mut self,
tu: &'temp ast::TranslationUnit<'source>,
) -> Result<crate::Module, Error<'source>> {
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,

View File

@ -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<ast::Type<'a>>` values.
/// User-defined types are referred to by name until lowering.
pub types: Arena<Type<'a>>,
/// 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<DiagnosticFilterNode>,
/// 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<Handle<DiagnosticFilterNode>>,
}
#[derive(Debug, Clone, Copy)]

View File

@ -1,5 +1,5 @@
use crate::diagnostic_filter::{
self, DiagnosticFilter, DiagnosticFilterMap, FilterableTriggeringRule,
self, DiagnosticFilter, DiagnosticFilterMap, DiagnosticFilterNode, FilterableTriggeringRule,
};
use crate::front::wgsl::error::{Error, ExpectedToken};
use crate::front::wgsl::parse::directive::enable_extension::{
@ -2581,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) {
@ -2672,4 +2674,25 @@ impl Parser {
Ok(filter)
}
pub(crate) fn write_diagnostic_filters(
arena: &mut Arena<DiagnosticFilterNode>,
filters: DiagnosticFilterMap,
parent: Option<Handle<DiagnosticFilterNode>>,
) -> Option<Handle<DiagnosticFilterNode>> {
filters
.into_iter()
.fold(parent, |parent, (triggering_rule, (new_severity, span))| {
Some(arena.append(
DiagnosticFilterNode {
inner: DiagnosticFilter {
new_severity,
triggering_rule,
},
parent,
},
span,
))
})
}
}

View File

@ -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<Function>,
/// Entry points.
pub entry_points: Vec<EntryPoint>,
/// 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<DiagnosticFilterNode>,
/// 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<Handle<DiagnosticFilterNode>>,
}

View File

@ -6,6 +6,7 @@
//! - expression reference counts
use super::{ExpressionError, FunctionError, ModuleInfo, ShaderStages, ValidationFlags};
use crate::diagnostic_filter::DiagnosticFilterNode;
use crate::span::{AddSpan as _, WithSpan};
use crate::{
arena::{Arena, Handle},
@ -289,6 +290,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<Handle<DiagnosticFilterNode>>,
}
impl FunctionInfo {
@ -820,12 +828,14 @@ impl FunctionInfo {
/// Returns a `NonUniformControlFlow` error if any of the expressions in the block
/// require uniformity, but the current flow is non-uniform.
#[allow(clippy::or_fun_call)]
#[allow(clippy::only_used_in_recursion)]
fn process_block(
&mut self,
statements: &crate::Block,
other_functions: &[FunctionInfo],
mut disruptor: Option<UniformityDisruptor>,
expression_arena: &Arena<crate::Expression>,
diagnostic_filter_arena: &Arena<DiagnosticFilterNode>,
) -> Result<FunctionUniformity, WithSpan<FunctionError>> {
use crate::Statement as S;
@ -901,9 +911,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 +931,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 +957,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 +973,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 +1140,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 +1164,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 +1260,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 +1307,8 @@ fn uniform_control_flow() {
&vec![stmt_emit1, stmt_if_uniform].into(),
&[],
None,
&expressions
&expressions,
&Arena::new(),
),
Ok(FunctionUniformity {
result: Uniformity {
@ -1308,6 +1340,7 @@ fn uniform_control_flow() {
&[],
None,
&expressions,
&Arena::new(),
);
if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE {
assert_eq!(info[derivative_expr].ref_count, 2);
@ -1335,7 +1368,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 +1396,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 {

View File

@ -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(())
}

View File

@ -0,0 +1,2 @@
(
)

View File

@ -0,0 +1 @@
diagnostic(off, derivative_uniformity);

View File

@ -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: [

View File

@ -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: [],

View File

@ -163,6 +163,7 @@
],
sampling: [],
dual_source_blending: false,
diagnostic_filter_leaf: None,
),
],
const_expression_types: [

View File

@ -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: [

View File

@ -2476,4 +2476,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -2476,4 +2476,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -279,4 +279,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -304,4 +304,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -330,4 +330,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -330,4 +330,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -51,4 +51,6 @@
),
],
entry_points: [],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -51,4 +51,6 @@
),
],
entry_points: [],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -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),
)

View File

@ -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),
)

View File

@ -192,4 +192,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -262,4 +262,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -369,4 +369,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -369,4 +369,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -136,4 +136,6 @@
),
],
entry_points: [],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -136,4 +136,6 @@
),
],
entry_points: [],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -125,4 +125,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -125,4 +125,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -256,4 +256,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -256,4 +256,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -196,4 +196,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -196,4 +196,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -1026,4 +1026,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -1304,4 +1304,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -609,4 +609,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -715,4 +715,6 @@
),
),
],
diagnostic_filters: [],
diagnostic_filter_leaf: None,
)

View File

@ -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() {