spirv-fuzz: Do not make synonyms of void result ids (#3747)

Fixes #3746.
diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
index 9262f6f..877a38c 100644
--- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
+++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
@@ -768,6 +768,13 @@
   if (end_type_id_1 == 0 || end_type_id_2 == 0) {
     return false;
   }
+  // Neither end type is allowed to be void.
+  if (context->get_def_use_mgr()->GetDef(end_type_id_1)->opcode() ==
+          SpvOpTypeVoid ||
+      context->get_def_use_mgr()->GetDef(end_type_id_2)->opcode() ==
+          SpvOpTypeVoid) {
+    return false;
+  }
   // If the end types are the same, the data descriptors are comparable.
   if (end_type_id_1 == end_type_id_2) {
     return true;
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 768e714..db77f5d 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -271,6 +271,11 @@
     return false;
   }
   auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+  if (type_inst->opcode() == SpvOpTypeVoid) {
+    // We only make synonyms of instructions that define objects, and an object
+    // cannot have void type.
+    return false;
+  }
   if (type_inst->opcode() == SpvOpTypePointer) {
     switch (inst->opcode()) {
       case SpvOpConstantNull:
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp
index 1adc948..93253a1 100644
--- a/test/fuzz/transformation_add_synonym_test.cpp
+++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -1392,6 +1392,45 @@
           .IsApplicable(context.get(), transformation_context));
 }
 
+TEST(TransformationAddSynonym, DoNotCopyVoidRunctionResult) {
+  // This checks that we do not try to copy the result of a void function.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpName %4 "main"
+               OpName %6 "foo("
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpFunctionCall %2 %6
+               OpReturn
+               OpFunctionEnd
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+  spvtools::ValidatorOptions validator_options;
+  TransformationContext transformation_context(&fact_manager,
+                                               validator_options);
+
+  ASSERT_FALSE(TransformationAddSynonym(
+                   8, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
+                   MakeInstructionDescriptor(8, SpvOpReturn, 0))
+                   .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools