diff --git a/frontend/lib/resolution/InitResolver.cpp b/frontend/lib/resolution/InitResolver.cpp index a95db6ea8b7a..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,13 +770,15 @@ 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 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!"); @@ -890,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 471762d746f6..73450a9a9129 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 initPoints; + static owned create(Context* context, Resolver& visitor, const uast::Function* fn); @@ -155,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 30b2bcdad1de..3bd1474416d6 100644 --- a/frontend/lib/resolution/call-init-deinit.cpp +++ b/frontend/lib/resolution/call-init-deinit.cpp @@ -899,11 +899,13 @@ void CallInitDeinit::handleAssign(const OpCall* ast, RV& rv) { // check for use of deinited variables processMentions(ast, rv); - + + bool isIniting = splitInited; + isIniting |= resolver.initResolver && resolver.initResolver->isInitPoint(ast); if (lhsType.isType() || lhsType.isParam()) { // these are basically 'move' initialization resolveMoveInit(ast, rhsAst, lhsType, rhsType, rv); - } else if (splitInited) { + } else if (isIniting) { processInit(frame, ast, lhsType, rhsType, rv); } else { // it is assignment, so resolve the '=' call diff --git a/frontend/test/resolution/testInitSemantics.cpp b/frontend/test/resolution/testInitSemantics.cpp index 28d5a22321c7..3fe0615b4f8e 100644 --- a/frontend/test/resolution/testInitSemantics.cpp +++ b/frontend/test/resolution/testInitSemantics.cpp @@ -1627,6 +1627,37 @@ 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); + 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 @@ -1673,6 +1704,7 @@ int main() { testInitGenericAfterConcrete(); + testNilFieldInit(); return 0; }