Handle no index access chain in local access chain convert (#3678)

* Handle no index access chain in local access chain convert

Fixes #3643
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 9b8c112..9ff4ec6 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -77,6 +77,15 @@
 bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
     const Instruction* address_inst, Instruction* original_load) {
   // Build and append load of variable in ptrInst
+  if (address_inst->NumInOperands() == 1) {
+    // An access chain with no indices is essentially a copy.  All that is
+    // needed is to propagate the address.
+    context()->ReplaceAllUsesWith(
+        address_inst->result_id(),
+        address_inst->GetSingleWordInOperand(kAccessChainPtrIdInIdx));
+    return true;
+  }
+
   std::vector<std::unique_ptr<Instruction>> new_inst;
   uint32_t varId;
   uint32_t varPteTypeId;
@@ -109,6 +118,18 @@
 bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
     const Instruction* ptrInst, uint32_t valId,
     std::vector<std::unique_ptr<Instruction>>* newInsts) {
+  if (ptrInst->NumInOperands() == 1) {
+    // An access chain with no indices is essentially a copy.  However, we still
+    // have to create a new store because the old ones will be deleted.
+    BuildAndAppendInst(
+        SpvOpStore, 0, 0,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+          {ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}},
+        newInsts);
+    return true;
+  }
+
   // Build and append load of variable in ptrInst
   uint32_t varId;
   uint32_t varPteTypeId;
@@ -246,11 +267,13 @@
           if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) {
             return Status::Failure;
           }
+          size_t num_of_instructions_to_skip = newInsts.size() - 1;
           dead_instructions.push_back(&*ii);
           ++ii;
           ii = ii.InsertBefore(std::move(newInsts));
-          ++ii;
-          ++ii;
+          for (size_t i = 0; i < num_of_instructions_to_skip; ++i) {
+            ++ii;
+          }
           modified = true;
         } break;
         default:
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index 39899e3..3161d90 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -927,6 +927,37 @@
   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
 }
 
+TEST_F(LocalAccessChainConvertTest, AccessChainWithNoIndex) {
+  const std::string before =
+      R"(
+; CHECK: OpFunction
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: OpStore [[var]] %true
+; CHECK: OpLoad %bool [[var]]
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource ESSL 310
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+%_ptr_Function_bool = OpTypePointer Function %bool
+          %2 = OpFunction %void None %4
+          %8 = OpLabel
+          %9 = OpVariable %_ptr_Function_bool Function
+         %10 = OpAccessChain %_ptr_Function_bool %9
+               OpStore %10 %true
+         %11 = OpLoad %bool %10
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalAccessChainConvertPass>(before, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Assorted vector and matrix types