From 99f4c484c208bafcde855e8a25c92445bd82a3f6 Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Wed, 2 Oct 2024 11:52:24 -0700
Subject: [PATCH 1/7] code for the resolution, but the opcall intents are still
 not sorted. --- Signed-off-by: Brandon Neth <brandon.neth@hpe.com>

---
 frontend/lib/resolution/InitResolver.cpp     | 10 +++++++---
 frontend/lib/resolution/InitResolver.h       |  3 +++
 frontend/lib/resolution/call-init-deinit.cpp |  5 ++++-
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/frontend/lib/resolution/InitResolver.cpp b/frontend/lib/resolution/InitResolver.cpp
index a95db6ea8b7a..40aee6fda1ca 100644
--- a/frontend/lib/resolution/InitResolver.cpp
+++ b/frontend/lib/resolution/InitResolver.cpp
@@ -769,13 +769,17 @@ bool InitResolver::handleAssignmentToField(const OpCall* node) {
       // field doesn't contribute to the receiver type.
       updateResolverVisibleReceiverType();
 
-      auto lhsKind = state->qt.kind();
-      if (lhsKind != QualifiedType::TYPE && lhsKind != QualifiedType::PARAM) {
+      auto reLhs = initResolver_.byPostorder.byId(lhs->id());
+      auto lhsQt = reLhs.type();
+
+      auto lhsKind = lhsQt.kind();
+      if (lhsKind != QualifiedType::TYPE && lhsKind != QualifiedType::PARAM && isConstQualifier(lhsKind)) {
         // Regardless of the field's intent, it is mutable in this expression.
         lhsKind = QualifiedType::REF;
       }
-      auto lhsType = QualifiedType(lhsKind, state->qt.type(), state->qt.param());
+      auto lhsType = QualifiedType(lhsKind, lhsQt.type(), lhsQt.param());
       initResolver_.byPostorder.byAst(lhs).setType(lhsType);
+      initPoints.insert(node);
 
     } else {
       CHPL_ASSERT(0 == "Not handled yet!");
diff --git a/frontend/lib/resolution/InitResolver.h b/frontend/lib/resolution/InitResolver.h
index 471762d746f6..2c61db7dc9c8 100644
--- a/frontend/lib/resolution/InitResolver.h
+++ b/frontend/lib/resolution/InitResolver.h
@@ -129,6 +129,9 @@ class InitResolver {
 
 public:
 
+  //initialization points to guide handling `=` operators 
+  std::set<const uast::AstNode*> initPoints;
+
   static owned<InitResolver>
   create(Context* context, Resolver& visitor, const uast::Function* fn);
 
diff --git a/frontend/lib/resolution/call-init-deinit.cpp b/frontend/lib/resolution/call-init-deinit.cpp
index 30b2bcdad1de..f3260de622be 100644
--- a/frontend/lib/resolution/call-init-deinit.cpp
+++ b/frontend/lib/resolution/call-init-deinit.cpp
@@ -899,12 +899,15 @@ void CallInitDeinit::handleAssign(const OpCall* ast, RV& rv) {
 
   // check for use of deinited variables
   processMentions(ast, rv);
-
+  
+  bool isIniting = resolver.initResolver->initPoints.count(ast) > 0;
   if (lhsType.isType() || lhsType.isParam()) {
     // these are basically 'move' initialization
     resolveMoveInit(ast, rhsAst, lhsType, rhsType, rv);
   } else if (splitInited) {
     processInit(frame, ast, lhsType, rhsType, rv);
+  } else if (isIniting) {
+    processInit(frame, ast, lhsType, rhsType, rv);
   } else {
     // it is assignment, so resolve the '=' call
     resolveAssign(ast, lhsType, rhsType, rv);

From d6842be3bffe1a8ca9ba8edc7d9d4e050184abf9 Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Wed, 2 Oct 2024 11:54:49 -0700
Subject: [PATCH 2/7] simplified logic. --- Signed-off-by: Brandon Neth
 <brandon.neth@hpe.com>

---
 frontend/lib/resolution/call-init-deinit.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/frontend/lib/resolution/call-init-deinit.cpp b/frontend/lib/resolution/call-init-deinit.cpp
index f3260de622be..b4f7b6ad3037 100644
--- a/frontend/lib/resolution/call-init-deinit.cpp
+++ b/frontend/lib/resolution/call-init-deinit.cpp
@@ -900,12 +900,10 @@ void CallInitDeinit::handleAssign(const OpCall* ast, RV& rv) {
   // check for use of deinited variables
   processMentions(ast, rv);
   
-  bool isIniting = resolver.initResolver->initPoints.count(ast) > 0;
+  bool isIniting = splitInited || resolver.initResolver->initPoints.count(ast) > 0;
   if (lhsType.isType() || lhsType.isParam()) {
     // these are basically 'move' initialization
     resolveMoveInit(ast, rhsAst, lhsType, rhsType, rv);
-  } else if (splitInited) {
-    processInit(frame, ast, lhsType, rhsType, rv);
   } else if (isIniting) {
     processInit(frame, ast, lhsType, rhsType, rv);
   } else {

From d530bdedcd9549f723b2916bd2b2195275a106d2 Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Wed, 2 Oct 2024 12:35:49 -0700
Subject: [PATCH 3/7] test based on github issue code --- Signed-off-by:
 Brandon Neth <brandon.neth@hpe.com>

---
 .../test/resolution/testInitSemantics.cpp     | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/frontend/test/resolution/testInitSemantics.cpp b/frontend/test/resolution/testInitSemantics.cpp
index 28d5a22321c7..db5262e2ffcf 100644
--- a/frontend/test/resolution/testInitSemantics.cpp
+++ b/frontend/test/resolution/testInitSemantics.cpp
@@ -1627,6 +1627,28 @@ static void testInitGenericAfterConcrete() {
   }
 }
 
+static void testNilFieldInit() {
+  std::string program = R"""(
+    class C { var x: int; }
+
+    record R {
+      var x: unmanaged C?;
+      proc init() {
+        x = nil;
+      }
+    }
+ 
+    var x: R;
+  )""";
+  Context ctx;
+  Context* context = &ctx;
+  ErrorGuard guard(context);
+  auto t = resolveTypeOfX(context, program);
+  assert(t);
+  assert(t->isRecordType());
+
+  assert(guard.errors().size() == 0);
+};
 // TODO:
 // - test using defaults for types and params
 //   - also in conditionals
@@ -1673,6 +1695,7 @@ int main() {
 
   testInitGenericAfterConcrete();
 
+  testNilFieldInit();
   return 0;
 }
 

From bbc7c26de134fa23f5b1f77c2795a56f89a9b35f Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Wed, 2 Oct 2024 13:28:31 -0700
Subject: [PATCH 4/7] updated tests --- Signed-off-by: Brandon Neth
 <brandon.neth@hpe.com>

---
 frontend/test/resolution/testInitSemantics.cpp | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/frontend/test/resolution/testInitSemantics.cpp b/frontend/test/resolution/testInitSemantics.cpp
index db5262e2ffcf..3fe0615b4f8e 100644
--- a/frontend/test/resolution/testInitSemantics.cpp
+++ b/frontend/test/resolution/testInitSemantics.cpp
@@ -1643,12 +1643,21 @@ static void testNilFieldInit() {
   Context ctx;
   Context* context = &ctx;
   ErrorGuard guard(context);
-  auto t = resolveTypeOfX(context, program);
-  assert(t);
-  assert(t->isRecordType());
+  ResolutionContext rcval(context);
+  auto rc = &rcval;
+  auto m = parseModule(context, std::move(program));
+  auto recordDecl = m->stmt(1)->toAggregateDecl();
+  auto initFunc = recordDecl->declOrComment(1)->toFunction();
+  assert(initFunc);
+
+  auto untyped = UntypedFnSignature::get(context, initFunc);
+  auto typed = typedSignatureInitial(rc, untyped);
+  auto inFn = resolveFunction(rc, typed, nullptr);
+  assert(inFn);
 
   assert(guard.errors().size() == 0);
 };
+
 // TODO:
 // - test using defaults for types and params
 //   - also in conditionals

From 538f734f46baeedededc32f179d1e568bd34a7e2 Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Wed, 2 Oct 2024 13:40:18 -0700
Subject: [PATCH 5/7] fixed nullptr segfault --- Signed-off-by: Brandon Neth
 <brandon.neth@hpe.com>

---
 frontend/lib/resolution/call-init-deinit.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/frontend/lib/resolution/call-init-deinit.cpp b/frontend/lib/resolution/call-init-deinit.cpp
index b4f7b6ad3037..9e1c4562e9e1 100644
--- a/frontend/lib/resolution/call-init-deinit.cpp
+++ b/frontend/lib/resolution/call-init-deinit.cpp
@@ -900,7 +900,8 @@ void CallInitDeinit::handleAssign(const OpCall* ast, RV& rv) {
   // check for use of deinited variables
   processMentions(ast, rv);
   
-  bool isIniting = splitInited || resolver.initResolver->initPoints.count(ast) > 0;
+  bool isIniting = splitInited;
+  isIniting |= resolver.initResolver && resolver.initResolver->initPoints.count(ast) > 0;
   if (lhsType.isType() || lhsType.isParam()) {
     // these are basically 'move' initialization
     resolveMoveInit(ast, rhsAst, lhsType, rhsType, rv);

From 7f81b4968b52071f2725e041cd90a10021bec9cc Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Wed, 2 Oct 2024 15:06:13 -0700
Subject: [PATCH 6/7] whitespace and matching lhs to rhs --- Signed-off-by:
 Brandon Neth <brandon.neth@hpe.com>

---
 frontend/lib/resolution/InitResolver.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/frontend/lib/resolution/InitResolver.cpp b/frontend/lib/resolution/InitResolver.cpp
index 40aee6fda1ca..5fe535be1dc6 100644
--- a/frontend/lib/resolution/InitResolver.cpp
+++ b/frontend/lib/resolution/InitResolver.cpp
@@ -769,9 +769,8 @@ bool InitResolver::handleAssignmentToField(const OpCall* node) {
       // field doesn't contribute to the receiver type.
       updateResolverVisibleReceiverType();
 
-      auto reLhs = initResolver_.byPostorder.byId(lhs->id());
+      auto& reLhs = initResolver_.byPostorder.byId(lhs->id());
       auto lhsQt = reLhs.type();
-
       auto lhsKind = lhsQt.kind();
       if (lhsKind != QualifiedType::TYPE && lhsKind != QualifiedType::PARAM && isConstQualifier(lhsKind)) {
         // Regardless of the field's intent, it is mutable in this expression.

From 0ee005a2aac7bad5cda879ebcef66e818510c294 Mon Sep 17 00:00:00 2001
From: Brandon Neth <brandon.neth@hpe.com>
Date: Thu, 10 Oct 2024 12:55:23 -0700
Subject: [PATCH 7/7] Ben's feedback on PR --- Signed-off-by: Brandon Neth
 <brandon.neth@hpe.com>

---
 frontend/lib/resolution/InitResolver.cpp     | 6 +++++-
 frontend/lib/resolution/InitResolver.h       | 3 +++
 frontend/lib/resolution/call-init-deinit.cpp | 2 +-
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/frontend/lib/resolution/InitResolver.cpp b/frontend/lib/resolution/InitResolver.cpp
index 5fe535be1dc6..48c92bb12b25 100644
--- a/frontend/lib/resolution/InitResolver.cpp
+++ b/frontend/lib/resolution/InitResolver.cpp
@@ -760,6 +760,7 @@ bool InitResolver::handleAssignmentToField(const OpCall* node) {
     // TODO: Anything to do if the opposite is true?
     if (!isAlreadyInitialized) {
       auto& reRhs = initResolver_.byPostorder.byAst(rhs);
+      auto& reLhs = initResolver_.byPostorder.byId(lhs->id());
       state->qt = reRhs.type();
       state->initPointId = node->id();
       state->isInitialized = true;
@@ -769,7 +770,6 @@ bool InitResolver::handleAssignmentToField(const OpCall* node) {
       // field doesn't contribute to the receiver type.
       updateResolverVisibleReceiverType();
 
-      auto& reLhs = initResolver_.byPostorder.byId(lhs->id());
       auto lhsQt = reLhs.type();
       auto lhsKind = lhsQt.kind();
       if (lhsKind != QualifiedType::TYPE && lhsKind != QualifiedType::PARAM && isConstQualifier(lhsKind)) {
@@ -893,5 +893,9 @@ void InitResolver::checkEarlyReturn(const Return* ret) {
   }
 }
 
+bool InitResolver::isInitPoint(const uast::AstNode* node) {
+  return initPoints.find(node) != initPoints.end();
+}
+
 } // end namespace resolution
 } // end namespace chpl
diff --git a/frontend/lib/resolution/InitResolver.h b/frontend/lib/resolution/InitResolver.h
index 2c61db7dc9c8..73450a9a9129 100644
--- a/frontend/lib/resolution/InitResolver.h
+++ b/frontend/lib/resolution/InitResolver.h
@@ -158,6 +158,9 @@ class InitResolver {
   const TypedFnSignature* finalize(void);
 
   void checkEarlyReturn(const uast::Return* ret);
+
+  // Returns true if the AST node is an initialization point
+  bool isInitPoint(const uast::AstNode* node);
 };
 
 } // end namespace resolution
diff --git a/frontend/lib/resolution/call-init-deinit.cpp b/frontend/lib/resolution/call-init-deinit.cpp
index 9e1c4562e9e1..3bd1474416d6 100644
--- a/frontend/lib/resolution/call-init-deinit.cpp
+++ b/frontend/lib/resolution/call-init-deinit.cpp
@@ -901,7 +901,7 @@ void CallInitDeinit::handleAssign(const OpCall* ast, RV& rv) {
   processMentions(ast, rv);
   
   bool isIniting = splitInited;
-  isIniting |= resolver.initResolver && resolver.initResolver->initPoints.count(ast) > 0;
+  isIniting |= resolver.initResolver && resolver.initResolver->isInitPoint(ast);
   if (lhsType.isType() || lhsType.isParam()) {
     // these are basically 'move' initialization
     resolveMoveInit(ast, rhsAst, lhsType, rhsType, rv);