spirvfuzz: Fix type-related bug, change undef to zero, and add assert (#3188)

This fixes a bug where the type id of a type instruction, rather than
its result id, was being used.  It also favours using zero as the
return value when replacing an OpKill or OpUnreachable with a return
instruction, and adds a check that the donor module is valid when
doing module donation.

Fixes #3187.
diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp
index 3368665..27d8a6e 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -62,6 +62,8 @@
     std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
         GetFuzzerContext()->RandomIndex(donor_suppliers_))();
     assert(donor_ir_context != nullptr && "Supplying of donor failed");
+    assert(fuzzerutil::IsValid(donor_ir_context.get()) &&
+           "The donor module must be valid");
     // Donate the supplied module.
     //
     // Randomly decide whether to make the module livesafe (see
@@ -668,9 +670,12 @@
         // The return type is void, so we don't need a return value.
         kill_unreachable_return_value_id = 0;
       } else {
-        // We do need a return value; we use OpUndef.
+        // We do need a return value; we use zero.
+        assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
+               "Function return type must not be a pointer.");
         kill_unreachable_return_value_id =
-            FindOrCreateGlobalUndef(function_return_type_inst->type_id());
+            FindOrCreateZeroConstant(original_id_to_donated_id->at(
+                function_return_type_inst->result_id()));
       }
       // Add the function in a livesafe manner.
       ApplyTransformation(TransformationAddFunction(