diff --git a/naga/src/ir/mod.rs b/naga/src/ir/mod.rs index d7452552ac8..d8f5c961ff2 100644 --- a/naga/src/ir/mod.rs +++ b/naga/src/ir/mod.rs @@ -2411,6 +2411,12 @@ pub struct FunctionResult { #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] pub struct Function { /// Name of the function, if any. + /// + /// Unlike WGSL, Naga IR allows a module to have multiple functions with the + /// same name. Since functions are generally identified by handle, the name + /// is mostly needed for diagnostics and as a hint to [`Namer`]. + /// + /// [`Namer`]: crate::proc::Namer pub name: Option, /// Information about function argument. pub arguments: Vec, @@ -2502,7 +2508,9 @@ pub struct Function { pub struct EntryPoint { /// Name of this entry point, visible externally. /// - /// Entry point names for a given `stage` must be distinct within a module. + /// Unlike WGSL, Naga IR allows a module to have multiple entry points with + /// the same name, as long as they are for different shader stages. That is, + /// `(name, stage)` pairs must be distinct within a module. pub name: String, /// Shader stage. pub stage: ShaderStage, diff --git a/naga/tests/naga/validation.rs b/naga/tests/naga/validation.rs index bb22b70adcc..b6e32fc40ea 100644 --- a/naga/tests/naga/validation.rs +++ b/naga/tests/naga/validation.rs @@ -1753,3 +1753,149 @@ fn memory_decorations_require_storage_address_space() { } )); } + +/// Naga validation should permit multiple entry points to have the same name, +/// as long as they are for distinct stages. +/// +/// WGSL does not permit conflicting names; this is a case where Naga IR is +/// looser than WGSL. +#[test] +fn entry_points_distinguished_by_stage() { + let mut test_spans = TestSpanGenerator::default(); + let mut module = Module::default(); + + let ty_vec4f = module.types.insert( + ir::Type { + name: Some("vec4f".to_string()), + inner: ir::TypeInner::Vector { + size: ir::VectorSize::Quad, + scalar: ir::Scalar::F32, + }, + }, + test_spans.next(), + ); + + let vertex_function = ir::Function { + name: Some("non_unique_name".into()), + result: Some(ir::FunctionResult { + ty: ty_vec4f, + binding: Some(ir::Binding::BuiltIn(ir::BuiltIn::Position { + invariant: false, + })), + }), + ..ir::Function::default() + }; + module.entry_points.push(ir::EntryPoint { + name: "non_unique_name".into(), + stage: ir::ShaderStage::Vertex, + early_depth_test: None, + workgroup_size: [0, 0, 0], + workgroup_size_overrides: None, + function: vertex_function, + mesh_info: None, + task_payload: None, + incoming_ray_payload: None, + }); + + module.entry_points.push(ir::EntryPoint { + name: "non_unique_name".into(), + stage: ir::ShaderStage::Compute, + early_depth_test: None, + workgroup_size: [1, 1, 1], + workgroup_size_overrides: None, + function: ir::Function::default(), + mesh_info: None, + task_payload: None, + incoming_ray_payload: None, + }); + + valid::Validator::new( + valid::ValidationFlags::default(), + valid::Capabilities::default(), + ) + .validate(&module) + .expect("module should be valid"); +} + +/// Naga validation should not allow a `Module` to have multiple entry points +/// with the same name and the same stage. +#[test] +fn entry_points_share_name() { + let mut module = Module::default(); + + module.entry_points.push(ir::EntryPoint { + name: "non_unique_name".into(), + stage: ir::ShaderStage::Compute, + early_depth_test: None, + workgroup_size: [1, 1, 1], + workgroup_size_overrides: None, + function: ir::Function::default(), + mesh_info: None, + task_payload: None, + incoming_ray_payload: None, + }); + + module.entry_points.push(ir::EntryPoint { + name: "non_unique_name".into(), + stage: ir::ShaderStage::Compute, + early_depth_test: None, + workgroup_size: [1, 1, 1], + workgroup_size_overrides: None, + function: ir::Function::default(), + mesh_info: None, + task_payload: None, + incoming_ray_payload: None, + }); + + let err = valid::Validator::new( + valid::ValidationFlags::default(), + valid::Capabilities::default(), + ) + .validate(&module) + .expect_err("module should be invalid"); + + assert!(matches!( + err.into_inner(), + valid::ValidationError::EntryPoint { + source: valid::EntryPointError::Conflict, + .. + } + )); +} + +/// Naga validation should permit a `Module` to have multiple non-entry-point +/// functions with the same name. +/// +/// WGSL does not allow this, but Naga IR does: it always refers to functions by +/// handle, so the names aren't actually needed to interpret the module's +/// contents. +#[test] +fn functions_share_name() { + let mut test_spans = TestSpanGenerator::default(); + let mut module = Module::default(); + + module.functions.append( + ir::Function { + name: Some("non_unique_name".into()), + result: None, + ..ir::Function::default() + }, + test_spans.next(), + ); + + module.functions.append( + ir::Function { + name: Some("non_unique_name".into()), + result: None, + ..ir::Function::default() + }, + test_spans.next(), + ); + + valid::Validator::new( + valid::ValidationFlags::default(), + valid::Capabilities::default(), + ) + .validate(&module) + .expect("module should be valid"); +}