From 77bb66d55c6eedbd51cae0c1cdf903984a543dab Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 8 Jan 2025 14:54:34 -0700 Subject: [PATCH 01/16] add bad split-init actual errors Signed-off-by: Ahmad Rezaii --- .../resolution-error-classes-list.h | 2 +- frontend/lib/resolution/Resolver.cpp | 38 ++++++++++++++++++- .../resolution-error-classes-list.cpp | 27 ++++++++++--- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/frontend/include/chpl/resolution/resolution-error-classes-list.h b/frontend/include/chpl/resolution/resolution-error-classes-list.h index 06da61c438c3..d6eb1d9a48bb 100644 --- a/frontend/include/chpl/resolution/resolution-error-classes-list.h +++ b/frontend/include/chpl/resolution/resolution-error-classes-list.h @@ -80,7 +80,7 @@ ERROR_CLASS(MultipleEnumElems, const uast::AstNode*, chpl::UniqueString, const u ERROR_CLASS(MultipleInheritance, const uast::Class*, const uast::AstNode*, const uast::AstNode*) ERROR_CLASS(MultipleQuestionArgs, const uast::FnCall*, const uast::AstNode*, const uast::AstNode*) ERROR_CLASS(NestedClassFieldRef, const uast::TypeDecl*, const uast::TypeDecl*, const uast::AstNode*, ID) -ERROR_CLASS(NoMatchingCandidates, const uast::AstNode*, resolution::CallInfo, std::vector) +ERROR_CLASS(NoMatchingCandidates, const uast::AstNode*, resolution::CallInfo, std::vector, std::vector, std::vector) ERROR_CLASS(NonClassInheritance, const uast::AggregateDecl*, const uast::AstNode*, const types::Type*) ERROR_CLASS(NonIterable, const uast::AstNode*, const uast::AstNode*, types::QualifiedType, std::vector>) ERROR_CLASS(NoMatchingEnumValue, const uast::AstNode*, const types::EnumType*, types::QualifiedType) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index d2eaa48672a6..4e2d692d0683 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -1898,9 +1898,11 @@ Resolver::issueErrorForFailedCallResolution(const uast::AstNode* astForErr, context->error(astForErr, "Cannot resolve call to '%s': ambiguity", ci.name().c_str()); } else { + std::vector uninitializedActuals; + std::vector faPairs; // could not find a most specific candidate std::vector rejected; - CHPL_REPORT(context, NoMatchingCandidates, astForErr, ci, rejected); + CHPL_REPORT(context, NoMatchingCandidates, astForErr, ci, rejected, faPairs, uninitializedActuals); } } else { context->error(astForErr, "Cannot establish type for call expression"); @@ -2043,7 +2045,39 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, if (!rejected.empty()) { // There were candidates but we threw them out. We can issue a nicer // error explaining why each candidate was rejected. - CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected); + std::vector badPasses; + std::vector actualDecls; + // check each rejected candidate for uninitialized actuals + for (auto& candidate : rejected) { + auto reason = candidate.reason(); + if (reason == resolution::FAIL_CANNOT_PASS && + /* skip printing detailed info_ here because computing the formal-actual + map will go poorly with an unknown formal. */ + candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { + auto fn = candidate.initialForErr(); + resolution::FormalActualMap fa(fn, ci); + auto badPass = fa.byFormalIdx(candidate.formalIdx()); + badPasses.push_back(badPass); + const uast::AstNode* actualExpr = nullptr; + const uast::VarLikeDecl* actualDecl = nullptr; + if (call && 0 <= badPass.actualIdx() && + badPass.actualIdx() < call->numActuals()) { + actualExpr = call->actual(badPass.actualIdx()); + } + // look for a definition point of the actual for error reporting of uninitialized vars + // typically in the case of bad split-initialization + if (actualExpr && actualExpr->isIdentifier()) { + auto& resolvedExpr = byPostorder.byAst(actualExpr->toIdentifier()); + if (auto id = resolvedExpr.toId()) { + auto var = parsing::idToAst(context, id); + // should put a nullptr if not a VarLikeDecl + actualDecl = var->toVarLikeDecl(); + } + } + actualDecls.push_back(actualDecl); + } + } + CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, badPasses, actualDecls); return; } } diff --git a/frontend/lib/resolution/resolution-error-classes-list.cpp b/frontend/lib/resolution/resolution-error-classes-list.cpp index ffdcac2ba463..8fb99e54d37a 100644 --- a/frontend/lib/resolution/resolution-error-classes-list.cpp +++ b/frontend/lib/resolution/resolution-error-classes-list.cpp @@ -646,6 +646,7 @@ static void printRejectedCandidates(ErrorWriterBase& wr, const char* expectedThing, GetActual&& getActual) { unsigned int printCount = 0; + unsigned int iterCount = 0; static const unsigned int maxPrintCount = 2; for (auto& candidate : rejected) { if (printCount == maxPrintCount) break; @@ -657,9 +658,8 @@ static void printRejectedCandidates(ErrorWriterBase& wr, /* skip printing detailed info_ here because computing the formal-actual map will go poorly with an unknown formal. */ candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { - auto fn = candidate.initialForErr(); - resolution::FormalActualMap fa(fn, ci); - auto badPass = fa.byFormalIdx(candidate.formalIdx()); + auto fn = candidate.initialForErr(); + auto badPass = formalActuals[iterCount]; auto formalDecl = badPass.formal(); const uast::AstNode* actualExpr = getActual(badPass.actualIdx()); @@ -672,19 +672,34 @@ static void printRejectedCandidates(ErrorWriterBase& wr, } else if (formalDecl->isTupleDecl()) { formalName = "'" + buildTupleDeclName(formalDecl->toTupleDecl()) + "'"; } - + bool actualPrinted = false; if (badPass.formalType().isUnknown()) { // The formal type can be unknown in an initial instantiation if it // depends on the previous formals' types. In that case, don't print it // and say something nicer. wr.message("The instantiated type of ", expectedThing, " ", formalName, " does not allow ", passedThing, "s of type '", badPass.actualType().type(), "'."); + } else if (badPass.actualType().isUnknown() && actualExpr && actualExpr->isIdentifier()) { + auto offendingActual = actualDecls[iterCount]; + auto formalKind = badPass.formalType().kind(); + auto actualName = "'" + actualExpr->toIdentifier()->name().str() + "'"; + wr.message("The actual ", actualName, + " expects to be split-initialized because it is declared without a type or initialization expression here:"); + wr.code(offendingActual, { offendingActual }); + wr.message("The call to ", ci.name() ," occurs before any valid initialization points:"); + wr.code(actualExpr, { actualExpr }); + actualPrinted =true; + wr.message("The call to '", ci.name(), "' cannot initialize ", + actualName, + " because only 'out' formals can be used to split-initialize. However, ", + actualName, " is passed to formal ", formalName, " which has intent '", formalKind, "'."); + } else { wr.message("The ", expectedThing, " ", formalName, " expects ", badPass.formalType(), ", but the ", passedThing, " was ", badPass.actualType(), "."); } - if (actualExpr) { + if (!actualPrinted && actualExpr) { wr.code(actualExpr, { actualExpr }); } @@ -1313,6 +1328,8 @@ void ErrorNoMatchingCandidates::write(ErrorWriterBase& wr) const { auto call = node->toCall(); auto& ci = std::get(info_); auto& rejected = std::get>(info_); + auto& formalActuals = std::get>(info_); + auto& actualDecls = std::get>(info_); wr.heading(kind_, type_, node, "unable to resolve call to '", ci.name(), "': no matching candidates."); wr.code(node); From 7c1405faa02c9c62d21fa6a80560858406bb9d86 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 8 Jan 2025 15:54:24 -0700 Subject: [PATCH 02/16] add note to error message output Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 4e2d692d0683..0964b9f01ae2 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -2049,11 +2049,10 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, std::vector actualDecls; // check each rejected candidate for uninitialized actuals for (auto& candidate : rejected) { - auto reason = candidate.reason(); - if (reason == resolution::FAIL_CANNOT_PASS && - /* skip printing detailed info_ here because computing the formal-actual + if (candidate.reason() == resolution::FAIL_CANNOT_PASS && + /* skip printing detailed info_ here because computing the formal-actual map will go poorly with an unknown formal. */ - candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { + candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto badPass = fa.byFormalIdx(candidate.formalIdx()); @@ -2064,8 +2063,8 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, badPass.actualIdx() < call->numActuals()) { actualExpr = call->actual(badPass.actualIdx()); } - // look for a definition point of the actual for error reporting of uninitialized vars - // typically in the case of bad split-initialization + // look for a definition point of the actual for error reporting of + // uninitialized vars typically in the case of bad split-initialization if (actualExpr && actualExpr->isIdentifier()) { auto& resolvedExpr = byPostorder.byAst(actualExpr->toIdentifier()); if (auto id = resolvedExpr.toId()) { From 2ea8ae1d38a4917a9009ca7d32aa39cf7a8ccc23 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 8 Jan 2025 17:30:28 -0700 Subject: [PATCH 03/16] deduplicate some control flow logic Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 0964b9f01ae2..27b390f1eb29 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -2047,8 +2047,12 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, // error explaining why each candidate was rejected. std::vector badPasses; std::vector actualDecls; + badPasses.resize(rejected.size()); + actualDecls.resize(rejected.size()); // check each rejected candidate for uninitialized actuals - for (auto& candidate : rejected) { + + for (size_t i = 0; i < rejected.size(); i++) { + auto& candidate = rejected[i]; if (candidate.reason() == resolution::FAIL_CANNOT_PASS && /* skip printing detailed info_ here because computing the formal-actual map will go poorly with an unknown formal. */ @@ -2056,7 +2060,7 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto badPass = fa.byFormalIdx(candidate.formalIdx()); - badPasses.push_back(badPass); + badPasses[i] =badPass; const uast::AstNode* actualExpr = nullptr; const uast::VarLikeDecl* actualDecl = nullptr; if (call && 0 <= badPass.actualIdx() && @@ -2073,9 +2077,10 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, actualDecl = var->toVarLikeDecl(); } } - actualDecls.push_back(actualDecl); + actualDecls[i] = actualDecl; } } + CHPL_ASSERT((badPasses.size() == rejected.size()) && (rejected.size() == actualDecls.size())); CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, badPasses, actualDecls); return; } From cee3ff461d5560e454f959514a7cab09a2331f4a Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 8 Jan 2025 17:35:40 -0700 Subject: [PATCH 04/16] fix some indentation Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 27b390f1eb29..8f8fd9d75268 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -2050,17 +2050,16 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, badPasses.resize(rejected.size()); actualDecls.resize(rejected.size()); // check each rejected candidate for uninitialized actuals - for (size_t i = 0; i < rejected.size(); i++) { auto& candidate = rejected[i]; if (candidate.reason() == resolution::FAIL_CANNOT_PASS && - /* skip printing detailed info_ here because computing the formal-actual - map will go poorly with an unknown formal. */ - candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { + /* skip printing detailed info_ here because computing the formal-actual + map will go poorly with an unknown formal. */ + candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto badPass = fa.byFormalIdx(candidate.formalIdx()); - badPasses[i] =badPass; + badPasses[i] = badPass; const uast::AstNode* actualExpr = nullptr; const uast::VarLikeDecl* actualDecl = nullptr; if (call && 0 <= badPass.actualIdx() && From 5d76eaa636da05b67098544b47ce2f5f4d80c225 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 8 Jan 2025 19:46:44 -0700 Subject: [PATCH 05/16] small refactor to extract helper, condition updates Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 86 ++++++++++++++++------------ 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 8f8fd9d75268..d2d9bc0d8a1a 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -984,6 +984,53 @@ static bool isCallToPtr(const AstNode* formalTypeExpr) { return false; } +// helper to gather bad actuals and report NoMatchingCandidates error +static void +handleRejectedCandidates(Context* context, + ResolutionResultByPostorderID& byPostorder, + std::vector& rejected, + const resolution::CallInfo& ci, + const uast::Call*& call) { + // There were candidates but we threw them out. We can issue a nicer + // error explaining why each candidate was rejected. + std::vector badPasses; + std::vector actualDecls; + badPasses.resize(rejected.size()); + actualDecls.resize(rejected.size()); + // check each rejected candidate for uninitialized actuals + for (size_t i = 0; i < rejected.size(); i++) { + auto &candidate = rejected[i]; + if (candidate.reason() == resolution::FAIL_CANNOT_PASS && + /* skip printing detailed info_ here because computing the formal-actual + map will go poorly with an unknown formal. */ + candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { + auto fn = candidate.initialForErr(); + resolution::FormalActualMap fa(fn, ci); + auto& badPass = fa.byFormalIdx(candidate.formalIdx()); + badPasses[i] = badPass; + const uast::AstNode *actualExpr = nullptr; + const uast::VarLikeDecl *actualDecl = nullptr; + if (call && 0 <= badPass.actualIdx() && + badPass.actualIdx() < call->numActuals()) { + actualExpr = call->actual(badPass.actualIdx()); + } + // look for a definition point of the actual for error reporting of + // uninitialized vars typically in the case of bad split-initialization + if (actualExpr && actualExpr->isIdentifier()) { + auto &resolvedExpr = byPostorder.byAst(actualExpr->toIdentifier()); + if (auto id = resolvedExpr.toId()) { + auto var = parsing::idToAst(context, id); + // should put a nullptr if not a VarLikeDecl + actualDecl = var->toVarLikeDecl(); + } + } + actualDecls[i] = actualDecl; + } + } + CHPL_ASSERT(badPasses.size() == rejected.size() && rejected.size() == actualDecls.size()); + CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, badPasses, actualDecls); +} + static void varArgTypeQueryError(Context* context, const AstNode* node, ResolvedExpression& result) { @@ -2043,44 +2090,7 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, } if (!rejected.empty()) { - // There were candidates but we threw them out. We can issue a nicer - // error explaining why each candidate was rejected. - std::vector badPasses; - std::vector actualDecls; - badPasses.resize(rejected.size()); - actualDecls.resize(rejected.size()); - // check each rejected candidate for uninitialized actuals - for (size_t i = 0; i < rejected.size(); i++) { - auto& candidate = rejected[i]; - if (candidate.reason() == resolution::FAIL_CANNOT_PASS && - /* skip printing detailed info_ here because computing the formal-actual - map will go poorly with an unknown formal. */ - candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { - auto fn = candidate.initialForErr(); - resolution::FormalActualMap fa(fn, ci); - auto badPass = fa.byFormalIdx(candidate.formalIdx()); - badPasses[i] = badPass; - const uast::AstNode* actualExpr = nullptr; - const uast::VarLikeDecl* actualDecl = nullptr; - if (call && 0 <= badPass.actualIdx() && - badPass.actualIdx() < call->numActuals()) { - actualExpr = call->actual(badPass.actualIdx()); - } - // look for a definition point of the actual for error reporting of - // uninitialized vars typically in the case of bad split-initialization - if (actualExpr && actualExpr->isIdentifier()) { - auto& resolvedExpr = byPostorder.byAst(actualExpr->toIdentifier()); - if (auto id = resolvedExpr.toId()) { - auto var = parsing::idToAst(context, id); - // should put a nullptr if not a VarLikeDecl - actualDecl = var->toVarLikeDecl(); - } - } - actualDecls[i] = actualDecl; - } - } - CHPL_ASSERT((badPasses.size() == rejected.size()) && (rejected.size() == actualDecls.size())); - CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, badPasses, actualDecls); + handleRejectedCandidates(context, byPostorder, rejected, ci, call); return; } } From e9063c01a4749319b6d355c2cec51a36e5ce2942 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 8 Jan 2025 19:52:54 -0700 Subject: [PATCH 06/16] update comment wording Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index d2d9bc0d8a1a..0dbc66548850 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -991,8 +991,8 @@ handleRejectedCandidates(Context* context, std::vector& rejected, const resolution::CallInfo& ci, const uast::Call*& call) { - // There were candidates but we threw them out. We can issue a nicer - // error explaining why each candidate was rejected. + // By performing some processing in the resolver, we can issue a nicer error + // explaining why each candidate was rejected. std::vector badPasses; std::vector actualDecls; badPasses.resize(rejected.size()); @@ -1001,8 +1001,8 @@ handleRejectedCandidates(Context* context, for (size_t i = 0; i < rejected.size(); i++) { auto &candidate = rejected[i]; if (candidate.reason() == resolution::FAIL_CANNOT_PASS && - /* skip printing detailed info_ here because computing the formal-actual - map will go poorly with an unknown formal. */ + /* skip computing the formal-actual map because it will go poorly + with an unknown formal. */ candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); @@ -2090,6 +2090,7 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, } if (!rejected.empty()) { + // There were candidates but we threw them out. Report on those. handleRejectedCandidates(context, byPostorder, rejected, ci, call); return; } From 86b632b6a1c28bcc2701da05f3e134a17c23caff Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Tue, 14 Jan 2025 12:02:57 -0700 Subject: [PATCH 07/16] pass adjusted list of Actual Ast nodes Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 27 ++++++++++++++++----------- frontend/lib/resolution/Resolver.h | 4 +++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 0dbc66548850..afdd03552138 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -990,7 +990,8 @@ handleRejectedCandidates(Context* context, ResolutionResultByPostorderID& byPostorder, std::vector& rejected, const resolution::CallInfo& ci, - const uast::Call*& call) { + const uast::Call*& call, + const std::vector& actualAsts) { // By performing some processing in the resolver, we can issue a nicer error // explaining why each candidate was rejected. std::vector badPasses; @@ -1011,8 +1012,8 @@ handleRejectedCandidates(Context* context, const uast::AstNode *actualExpr = nullptr; const uast::VarLikeDecl *actualDecl = nullptr; if (call && 0 <= badPass.actualIdx() && - badPass.actualIdx() < call->numActuals()) { - actualExpr = call->actual(badPass.actualIdx()); + badPass.actualIdx() < actualAsts.size()) { + actualExpr = actualAsts[badPass.actualIdx()]; } // look for a definition point of the actual for error reporting of // uninitialized vars typically in the case of bad split-initialization @@ -2071,6 +2072,7 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, const CallScopeInfo& inScopes, const QualifiedType& receiverType, const CallResolutionResult& c, + std::vector& actualAsts, optional actionAndId) { bool wasCallGenerated = (bool) actionAndId; CHPL_ASSERT(!wasCallGenerated || receiverType.isUnknown()); @@ -2091,7 +2093,7 @@ void Resolver::handleResolvedCallPrintCandidates(ResolvedExpression& r, if (!rejected.empty()) { // There were candidates but we threw them out. Report on those. - handleRejectedCandidates(context, byPostorder, rejected, ci, call); + handleRejectedCandidates(context, byPostorder, rejected, ci, call, actualAsts); return; } } @@ -2440,13 +2442,14 @@ bool Resolver::resolveSpecialNewCall(const Call* call) { bool isMethodCall = true; const AstNode* questionArg = nullptr; std::vector actuals; + std::vector actualAsts; // Prepare receiver. auto receiverInfo = CallInfoActual(calledType, USTR("this")); actuals.push_back(std::move(receiverInfo)); // Remaining actuals. - prepareCallInfoActuals(call, actuals, questionArg); + prepareCallInfoActuals(call, actuals, questionArg, &actualAsts); CHPL_ASSERT(!questionArg); // The 'new' will produce an 'init' call as a side effect. @@ -2460,8 +2463,9 @@ bool Resolver::resolveSpecialNewCall(const Call* call) { // note: the resolution machinery will get compiler generated candidates auto crr = resolveGeneratedCall(context, call, ci, inScopes); - handleResolvedCallPrintCandidates(re, call, ci, inScopes, QualifiedType(), crr, - { { AssociatedAction::NEW_INIT, call->id() } }); + optional action = { { AssociatedAction::NEW_INIT, call->id() } }; + handleResolvedCallPrintCandidates(re, call, ci, inScopes, QualifiedType(), + crr, actualAsts, action); // there should be one or zero applicable candidates @@ -2615,7 +2619,7 @@ bool Resolver::resolveSpecialKeywordCall(const Call* call) { DomainType::getDefaultDistType(context), UniqueString()); actuals.push_back(std::move(defaultDistArg)); // Remaining given args from domain() call as written - prepareCallInfoActuals(call, actuals, questionArg); + prepareCallInfoActuals(call, actuals, questionArg, /*actualAsts*/ nullptr); CHPL_ASSERT(!questionArg); auto ci = @@ -4526,11 +4530,12 @@ bool Resolver::enter(const Call* call) { void Resolver::prepareCallInfoActuals(const Call* call, std::vector& actuals, - const AstNode*& questionArg) { + const AstNode*& questionArg, + std::vector* actualAsts) { CallInfo::prepareActuals(context, call, byPostorder, /* raiseErrors */ true, actuals, questionArg, - /* actualAsts */ nullptr); + actualAsts); } static const Type* getGenericType(Context* context, const Type* recv) { @@ -4798,7 +4803,7 @@ void Resolver::handleCallExpr(const uast::Call* call) { rejected); // save the most specific candidates in the resolution result for the id - handleResolvedCallPrintCandidates(r, call, ci, inScopes, receiverType, c); + handleResolvedCallPrintCandidates(r, call, ci, inScopes, receiverType, c, actualAsts); // handle type inference for variables split-inited by 'out' formals adjustTypesForOutFormals(ci, actualAsts, c.mostSpecific()); diff --git a/frontend/lib/resolution/Resolver.h b/frontend/lib/resolution/Resolver.h index 813aef5c0016..39280799b2f9 100644 --- a/frontend/lib/resolution/Resolver.h +++ b/frontend/lib/resolution/Resolver.h @@ -488,6 +488,7 @@ struct Resolver { const CallScopeInfo& inScopes, const types::QualifiedType& receiverType, const CallResolutionResult& c, + std::vector& actualAsts, optional associatedActionAndId = {}); // If the variable with the passed ID has unknown or generic type, @@ -597,7 +598,8 @@ struct Resolver { // includes special handling for operators and tuple literals void prepareCallInfoActuals(const uast::Call* call, std::vector& actuals, - const uast::AstNode*& questionArg); + const uast::AstNode*& questionArg, + std::vector* actualAsts); // prepare a CallInfo by inspecting the called expression and actuals CallInfo prepareCallInfoNormalCall(const uast::Call* call); From 1400b593aa234c30e65ba9b88f97bb44f0001aa4 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Tue, 14 Jan 2025 12:25:26 -0700 Subject: [PATCH 08/16] fix size_t int compare Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index afdd03552138..61a34563622e 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -1012,7 +1012,7 @@ handleRejectedCandidates(Context* context, const uast::AstNode *actualExpr = nullptr; const uast::VarLikeDecl *actualDecl = nullptr; if (call && 0 <= badPass.actualIdx() && - badPass.actualIdx() < actualAsts.size()) { + (size_t)badPass.actualIdx() < actualAsts.size()) { actualExpr = actualAsts[badPass.actualIdx()]; } // look for a definition point of the actual for error reporting of From 719cc3afa2cddba7be95390671b50cf5db6cdac4 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Wed, 15 Jan 2025 22:17:51 -0700 Subject: [PATCH 09/16] add two tests for nomatchingcandidates Signed-off-by: Ahmad Rezaii --- .../resolution/noMatchingCandidates.1.good | 5 +++ .../resolution/noMatchingCandidates.2.good | 45 +++++++++++++++++++ .../resolution/noMatchingCandidates.chpl | 35 +++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 test/errors/resolution/noMatchingCandidates.1.good create mode 100644 test/errors/resolution/noMatchingCandidates.2.good create mode 100644 test/errors/resolution/noMatchingCandidates.chpl diff --git a/test/errors/resolution/noMatchingCandidates.1.good b/test/errors/resolution/noMatchingCandidates.1.good new file mode 100644 index 000000000000..5ae81eba4436 --- /dev/null +++ b/test/errors/resolution/noMatchingCandidates.1.good @@ -0,0 +1,5 @@ +noMatchingCandidates.chpl:6: error: unable to resolve call to 'fn': no matching candidates +noMatchingCandidates.chpl:2: note: the following candidate didn't match because an actual couldn't be passed to a formal +noMatchingCandidates.chpl:6: note: The actual 'x' expects to be split-initialized because it is declared without a type or initialization expression here +noMatchingCandidates.chpl:14: error: unable to resolve call to 'fn': no matching candidates +noMatchingCandidates.chpl:10: note: the following candidate didn't match because an actual couldn't be passed to a formal diff --git a/test/errors/resolution/noMatchingCandidates.2.good b/test/errors/resolution/noMatchingCandidates.2.good new file mode 100644 index 000000000000..325549c7d648 --- /dev/null +++ b/test/errors/resolution/noMatchingCandidates.2.good @@ -0,0 +1,45 @@ +─── error in noMatchingCandidates.chpl:6 [NoMatchingCandidates] ─── + Unable to resolve call to 'fn': no matching candidates. + | + 6 | fn(x); + | + + The following candidate didn't match because an actual couldn't be passed to a formal: + | + 2 | proc fn(const arg) { + | ⎺⎺⎺⎺⎺⎺⎺⎺⎺ + 3 | arg; + 4 | } + | + The actual 'x' expects to be split-initialized because it is declared without a type or initialization expression here: + | + 5 | var x; + | ⎺ + | + The call to 'fn' occurs before any valid initialization points: + | + 6 | fn(x); + | ⎺ + | + The call to 'fn' cannot initialize 'x' because only 'out' formals can be used to split-initialize. However, 'x' is passed to formal 'arg' which has intent 'const'. + +─── error in noMatchingCandidates.chpl:14 [NoMatchingCandidates] ─── + Unable to resolve call to 'fn': no matching candidates. + | + 14 | fn(x); + | + + The following candidate didn't match because an actual couldn't be passed to a formal: + | + 10 | proc fn(arg:string) { + | ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ + 11 | arg; + 12 | } + | + The formal 'arg' expects a value of type 'string', but the actual was a value of type 'int(64)'. + | + 14 | fn(x); + | ⎺ + | + Formals with kind 'const ref' expect the actual to be a subtype, but 'int(64)' is not a subtype of 'string'. + diff --git a/test/errors/resolution/noMatchingCandidates.chpl b/test/errors/resolution/noMatchingCandidates.chpl new file mode 100644 index 000000000000..e2487a499cce --- /dev/null +++ b/test/errors/resolution/noMatchingCandidates.chpl @@ -0,0 +1,35 @@ +{ // expects split-init + proc fn(const arg) { + arg; + } + var x; + fn(x); + x=5; +} +{ // type mismatch + proc fn(arg:string) { + arg; + } + var x=5; + fn(x); +} +// Tests needed: +// call was parenful but method is parenless + +// call was parenless but method was parenful + +// bad where clause + +// bad vararg count + +// star tuple mismatches + +// tuple size mismatches + +// The 'ref' intent requires the formal and actual types to match exactly + +// bad subtype + +// incompatible manager + +// incompatible nilability From 0c9f085947f20169494a40851a76ff3d72491ce0 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Fri, 17 Jan 2025 17:19:41 -0700 Subject: [PATCH 10/16] bring up to date with main Signed-off-by: Ahmad Rezaii --- .../resolution-error-classes-list.h | 2 +- frontend/lib/resolution/Resolver.cpp | 10 ++---- .../resolution-error-classes-list.cpp | 31 ++++++++++--------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/frontend/include/chpl/resolution/resolution-error-classes-list.h b/frontend/include/chpl/resolution/resolution-error-classes-list.h index d6eb1d9a48bb..8849912c57c6 100644 --- a/frontend/include/chpl/resolution/resolution-error-classes-list.h +++ b/frontend/include/chpl/resolution/resolution-error-classes-list.h @@ -80,7 +80,7 @@ ERROR_CLASS(MultipleEnumElems, const uast::AstNode*, chpl::UniqueString, const u ERROR_CLASS(MultipleInheritance, const uast::Class*, const uast::AstNode*, const uast::AstNode*) ERROR_CLASS(MultipleQuestionArgs, const uast::FnCall*, const uast::AstNode*, const uast::AstNode*) ERROR_CLASS(NestedClassFieldRef, const uast::TypeDecl*, const uast::TypeDecl*, const uast::AstNode*, ID) -ERROR_CLASS(NoMatchingCandidates, const uast::AstNode*, resolution::CallInfo, std::vector, std::vector, std::vector) +ERROR_CLASS(NoMatchingCandidates, const uast::AstNode*, resolution::CallInfo, std::vector, std::vector) ERROR_CLASS(NonClassInheritance, const uast::AggregateDecl*, const uast::AstNode*, const types::Type*) ERROR_CLASS(NonIterable, const uast::AstNode*, const uast::AstNode*, types::QualifiedType, std::vector>) ERROR_CLASS(NoMatchingEnumValue, const uast::AstNode*, const types::EnumType*, types::QualifiedType) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 61a34563622e..1ffdc5e27465 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -994,9 +994,7 @@ handleRejectedCandidates(Context* context, const std::vector& actualAsts) { // By performing some processing in the resolver, we can issue a nicer error // explaining why each candidate was rejected. - std::vector badPasses; std::vector actualDecls; - badPasses.resize(rejected.size()); actualDecls.resize(rejected.size()); // check each rejected candidate for uninitialized actuals for (size_t i = 0; i < rejected.size(); i++) { @@ -1008,7 +1006,6 @@ handleRejectedCandidates(Context* context, auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto& badPass = fa.byFormalIdx(candidate.formalIdx()); - badPasses[i] = badPass; const uast::AstNode *actualExpr = nullptr; const uast::VarLikeDecl *actualDecl = nullptr; if (call && 0 <= badPass.actualIdx() && @@ -1028,8 +1025,8 @@ handleRejectedCandidates(Context* context, actualDecls[i] = actualDecl; } } - CHPL_ASSERT(badPasses.size() == rejected.size() && rejected.size() == actualDecls.size()); - CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, badPasses, actualDecls); + CHPL_ASSERT(rejected.size() == actualDecls.size()); + CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, actualDecls); } static void varArgTypeQueryError(Context* context, @@ -1947,10 +1944,9 @@ Resolver::issueErrorForFailedCallResolution(const uast::AstNode* astForErr, ci.name().c_str()); } else { std::vector uninitializedActuals; - std::vector faPairs; // could not find a most specific candidate std::vector rejected; - CHPL_REPORT(context, NoMatchingCandidates, astForErr, ci, rejected, faPairs, uninitializedActuals); + CHPL_REPORT(context, NoMatchingCandidates, astForErr, ci, rejected, uninitializedActuals); } } else { context->error(astForErr, "Cannot establish type for call expression"); diff --git a/frontend/lib/resolution/resolution-error-classes-list.cpp b/frontend/lib/resolution/resolution-error-classes-list.cpp index 8fb99e54d37a..69111a33ffb3 100644 --- a/frontend/lib/resolution/resolution-error-classes-list.cpp +++ b/frontend/lib/resolution/resolution-error-classes-list.cpp @@ -644,7 +644,8 @@ static void printRejectedCandidates(ErrorWriterBase& wr, const char* passedThing, const char* expectedThingArticle, const char* expectedThing, - GetActual&& getActual) { + GetActual&& getActual, + const std::vector* actualDecls) { unsigned int printCount = 0; unsigned int iterCount = 0; static const unsigned int maxPrintCount = 2; @@ -658,8 +659,9 @@ static void printRejectedCandidates(ErrorWriterBase& wr, /* skip printing detailed info_ here because computing the formal-actual map will go poorly with an unknown formal. */ candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { - auto fn = candidate.initialForErr(); - auto badPass = formalActuals[iterCount]; + auto fn = candidate.initialForErr(); + resolution::FormalActualMap fa(fn, ci); + auto badPass = fa.byFormalIdx(candidate.formalIdx()); auto formalDecl = badPass.formal(); const uast::AstNode* actualExpr = getActual(badPass.actualIdx()); @@ -673,22 +675,25 @@ static void printRejectedCandidates(ErrorWriterBase& wr, formalName = "'" + buildTupleDeclName(formalDecl->toTupleDecl()) + "'"; } bool actualPrinted = false; + const uast::VarLikeDecl* offendingActual = actualDecls!=nullptr ? actualDecls->at(iterCount) : nullptr;\ if (badPass.formalType().isUnknown()) { // The formal type can be unknown in an initial instantiation if it // depends on the previous formals' types. In that case, don't print it // and say something nicer. wr.message("The instantiated type of ", expectedThing, " ", formalName, " does not allow ", passedThing, "s of type '", badPass.actualType().type(), "'."); - } else if (badPass.actualType().isUnknown() && actualExpr && actualExpr->isIdentifier()) { - auto offendingActual = actualDecls[iterCount]; + } else if (badPass.actualType().isUnknown() && + offendingActual && + !offendingActual->initExpression() && + !offendingActual->typeExpression()) { auto formalKind = badPass.formalType().kind(); auto actualName = "'" + actualExpr->toIdentifier()->name().str() + "'"; wr.message("The actual ", actualName, " expects to be split-initialized because it is declared without a type or initialization expression here:"); wr.code(offendingActual, { offendingActual }); - wr.message("The call to ", ci.name() ," occurs before any valid initialization points:"); + wr.message("The call to '", ci.name() ,"' occurs before any valid initialization points:"); wr.code(actualExpr, { actualExpr }); - actualPrinted =true; + actualPrinted = true; wr.message("The call to '", ci.name(), "' cannot initialize ", actualName, " because only 'out' formals can be used to split-initialize. However, ", @@ -847,10 +852,9 @@ void ErrorInterfaceMissingAssociatedType::write(ErrorWriterBase& wr) const { wr.note(implPoint, "while checking the implementation point here:"); wr.code(implPoint, { implPoint }); wr.message("Associated types are resolved as 'type' calls on types constrained by the interface."); - printRejectedCandidates(wr, implPoint, ci, rejected, "an", "actual", "a", "formal", [](int) -> const uast::AstNode* { return nullptr; - }); + }, nullptr); } void ErrorInterfaceMissingFn::write(ErrorWriterBase& wr) const { @@ -866,13 +870,12 @@ void ErrorInterfaceMissingFn::write(ErrorWriterBase& wr) const { wr.codeForDef(interface->id()); wr.note(implPoint, "while checking the implementation point here:"); wr.code(implPoint, { implPoint }); - - printRejectedCandidates(wr, implPoint, ci, rejected, "a", "required formal", "a", "cadidate formal", [fn](int idx) -> const uast::AstNode* { + printRejectedCandidates(wr, implPoint, ci, rejected, "a", "required formal", "a", "candidate formal", [fn](int idx) -> const uast::AstNode* { if (idx >= 0 && idx < fn->numFormals()) { return fn->untyped()->formalDecl(idx); } return nullptr; - }); + }, nullptr); } void ErrorInterfaceMultipleImplements::write(ErrorWriterBase& wr) const { @@ -1328,7 +1331,7 @@ void ErrorNoMatchingCandidates::write(ErrorWriterBase& wr) const { auto call = node->toCall(); auto& ci = std::get(info_); auto& rejected = std::get>(info_); - auto& formalActuals = std::get>(info_); + // auto& formalActuals = std::get>(info_); auto& actualDecls = std::get>(info_); wr.heading(kind_, type_, node, "unable to resolve call to '", ci.name(), "': no matching candidates."); @@ -1339,7 +1342,7 @@ void ErrorNoMatchingCandidates::write(ErrorWriterBase& wr) const { return call->actual(idx); } return nullptr; - }); + }, &actualDecls); } void ErrorNonClassInheritance::write(ErrorWriterBase& wr) const { From c81f5e368e84e8f2fcf8809ed878eeff256a93a3 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Fri, 17 Jan 2025 17:29:49 -0700 Subject: [PATCH 11/16] remove commented code, combine iter vars Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/resolution-error-classes-list.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/lib/resolution/resolution-error-classes-list.cpp b/frontend/lib/resolution/resolution-error-classes-list.cpp index 69111a33ffb3..07ba91a794cd 100644 --- a/frontend/lib/resolution/resolution-error-classes-list.cpp +++ b/frontend/lib/resolution/resolution-error-classes-list.cpp @@ -647,11 +647,9 @@ static void printRejectedCandidates(ErrorWriterBase& wr, GetActual&& getActual, const std::vector* actualDecls) { unsigned int printCount = 0; - unsigned int iterCount = 0; static const unsigned int maxPrintCount = 2; for (auto& candidate : rejected) { if (printCount == maxPrintCount) break; - printCount++; auto reason = candidate.reason(); wr.message(""); @@ -675,7 +673,9 @@ static void printRejectedCandidates(ErrorWriterBase& wr, formalName = "'" + buildTupleDeclName(formalDecl->toTupleDecl()) + "'"; } bool actualPrinted = false; - const uast::VarLikeDecl* offendingActual = actualDecls!=nullptr ? actualDecls->at(iterCount) : nullptr;\ + const uast::VarLikeDecl* offendingActual = actualDecls ? + actualDecls->at(printCount) : + nullptr; if (badPass.formalType().isUnknown()) { // The formal type can be unknown in an initial instantiation if it // depends on the previous formals' types. In that case, don't print it @@ -789,6 +789,7 @@ static void printRejectedCandidates(ErrorWriterBase& wr, } wr.code(candidate.idForErr()); } + printCount++; } if (printCount < rejected.size()) { @@ -1331,7 +1332,6 @@ void ErrorNoMatchingCandidates::write(ErrorWriterBase& wr) const { auto call = node->toCall(); auto& ci = std::get(info_); auto& rejected = std::get>(info_); - // auto& formalActuals = std::get>(info_); auto& actualDecls = std::get>(info_); wr.heading(kind_, type_, node, "unable to resolve call to '", ci.name(), "': no matching candidates."); From d2cc4896a09d393c96140ac96910b2577c9baff8 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Sat, 18 Jan 2025 06:17:55 -0700 Subject: [PATCH 12/16] small refactor to argument types, add method to ApplicabilityResult Signed-off-by: Ahmad Rezaii --- .../chpl/resolution/resolution-types.h | 5 ++++ frontend/lib/resolution/Resolver.cpp | 5 ++-- .../resolution-error-classes-list.cpp | 26 ++++++++----------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frontend/include/chpl/resolution/resolution-types.h b/frontend/include/chpl/resolution/resolution-types.h index b2d072a90ee6..2d8b13c44097 100644 --- a/frontend/include/chpl/resolution/resolution-types.h +++ b/frontend/include/chpl/resolution/resolution-types.h @@ -1431,6 +1431,11 @@ class ApplicabilityResult { PassingFailureReason formalReason() const { return formalReason_; } int formalIdx() const { return formalIdx_; } + + inline bool failedDueToWrongActual() const { + return candidateReason_ == resolution::FAIL_CANNOT_PASS && + formalReason_ != resolution::FAIL_UNKNOWN_FORMAL_TYPE; + } }; /** FormalActual holds information on a function formal and its binding (if any) */ diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 1ffdc5e27465..63b77d9c7d67 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -999,10 +999,9 @@ handleRejectedCandidates(Context* context, // check each rejected candidate for uninitialized actuals for (size_t i = 0; i < rejected.size(); i++) { auto &candidate = rejected[i]; - if (candidate.reason() == resolution::FAIL_CANNOT_PASS && - /* skip computing the formal-actual map because it will go poorly + if (/* skip computing the formal-actual map because it will go poorly with an unknown formal. */ - candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { + candidate.failedDueToWrongActual()) { auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto& badPass = fa.byFormalIdx(candidate.formalIdx()); diff --git a/frontend/lib/resolution/resolution-error-classes-list.cpp b/frontend/lib/resolution/resolution-error-classes-list.cpp index 07ba91a794cd..3e31d849fa7d 100644 --- a/frontend/lib/resolution/resolution-error-classes-list.cpp +++ b/frontend/lib/resolution/resolution-error-classes-list.cpp @@ -645,18 +645,15 @@ static void printRejectedCandidates(ErrorWriterBase& wr, const char* expectedThingArticle, const char* expectedThing, GetActual&& getActual, - const std::vector* actualDecls) { + const std::vector& actualDecls) { unsigned int printCount = 0; static const unsigned int maxPrintCount = 2; for (auto& candidate : rejected) { if (printCount == maxPrintCount) break; - - auto reason = candidate.reason(); wr.message(""); - if (reason == resolution::FAIL_CANNOT_PASS && - /* skip printing detailed info_ here because computing the formal-actual - map will go poorly with an unknown formal. */ - candidate.formalReason() != resolution::FAIL_UNKNOWN_FORMAL_TYPE) { + if (/* skip printing detailed info_ here because computing the formal-actual + map will go poorly with an unknown formal. */ + candidate.failedDueToWrongActual()) { auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto badPass = fa.byFormalIdx(candidate.formalIdx()); @@ -673,9 +670,7 @@ static void printRejectedCandidates(ErrorWriterBase& wr, formalName = "'" + buildTupleDeclName(formalDecl->toTupleDecl()) + "'"; } bool actualPrinted = false; - const uast::VarLikeDecl* offendingActual = actualDecls ? - actualDecls->at(printCount) : - nullptr; + const uast::VarLikeDecl* offendingActual = actualDecls.at(printCount); if (badPass.formalType().isUnknown()) { // The formal type can be unknown in an initial instantiation if it // depends on the previous formals' types. In that case, don't print it @@ -766,6 +761,7 @@ static void printRejectedCandidates(ErrorWriterBase& wr, } wr.code(candidate.idForErr()); } else { + auto reason = candidate.reason(); std::string reasonStr = ""; if (reason == resolution::FAIL_VARARG_MISMATCH) { reasonStr = "the number of varargs was incorrect:"; @@ -845,7 +841,7 @@ void ErrorInterfaceMissingAssociatedType::write(ErrorWriterBase& wr) const { auto var = std::get(info_); auto ci = std::get(info_); auto rejected = std::get>(info_); - + auto actualDecls = std::vector(rejected.size(), nullptr); wr.heading(kind_, type_, implPoint, "unable to find matching candidates for associated type '", var->name(), "'."); wr.codeForDef(var->id()); wr.message("Required by the interface '", interface->name(), "':"); @@ -855,7 +851,7 @@ void ErrorInterfaceMissingAssociatedType::write(ErrorWriterBase& wr) const { wr.message("Associated types are resolved as 'type' calls on types constrained by the interface."); printRejectedCandidates(wr, implPoint, ci, rejected, "an", "actual", "a", "formal", [](int) -> const uast::AstNode* { return nullptr; - }, nullptr); + }, actualDecls); } void ErrorInterfaceMissingFn::write(ErrorWriterBase& wr) const { @@ -864,7 +860,7 @@ void ErrorInterfaceMissingFn::write(ErrorWriterBase& wr) const { auto fn = std::get(info_); auto ci = std::get(info_); auto rejected = std::get>(info_); - + auto actualDecls = std::vector(rejected.size(), nullptr); wr.heading(kind_, type_, implPoint, "unable to find matching candidates for function '", fn->untyped()->name(), "'."); wr.codeForDef(fn->id()); wr.message("Required by the interface '", interface->name(), "':"); @@ -876,7 +872,7 @@ void ErrorInterfaceMissingFn::write(ErrorWriterBase& wr) const { return fn->untyped()->formalDecl(idx); } return nullptr; - }, nullptr); + }, actualDecls); } void ErrorInterfaceMultipleImplements::write(ErrorWriterBase& wr) const { @@ -1342,7 +1338,7 @@ void ErrorNoMatchingCandidates::write(ErrorWriterBase& wr) const { return call->actual(idx); } return nullptr; - }, &actualDecls); + }, actualDecls); } void ErrorNonClassInheritance::write(ErrorWriterBase& wr) const { From 107f1cefd663ea4725b1f61111e26aad043b0ec9 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Mon, 27 Jan 2025 14:01:25 -0700 Subject: [PATCH 13/16] minor updates to address reviewer feedback Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 43 ++++++++++--------- .../resolution-error-classes-list.cpp | 2 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 63b77d9c7d67..1227f94c9583 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -1001,28 +1001,29 @@ handleRejectedCandidates(Context* context, auto &candidate = rejected[i]; if (/* skip computing the formal-actual map because it will go poorly with an unknown formal. */ - candidate.failedDueToWrongActual()) { - auto fn = candidate.initialForErr(); - resolution::FormalActualMap fa(fn, ci); - auto& badPass = fa.byFormalIdx(candidate.formalIdx()); - const uast::AstNode *actualExpr = nullptr; - const uast::VarLikeDecl *actualDecl = nullptr; - if (call && 0 <= badPass.actualIdx() && - (size_t)badPass.actualIdx() < actualAsts.size()) { - actualExpr = actualAsts[badPass.actualIdx()]; - } - // look for a definition point of the actual for error reporting of - // uninitialized vars typically in the case of bad split-initialization - if (actualExpr && actualExpr->isIdentifier()) { - auto &resolvedExpr = byPostorder.byAst(actualExpr->toIdentifier()); - if (auto id = resolvedExpr.toId()) { - auto var = parsing::idToAst(context, id); - // should put a nullptr if not a VarLikeDecl - actualDecl = var->toVarLikeDecl(); - } + !candidate.failedDueToWrongActual()) { + continue; + } + auto fn = candidate.initialForErr(); + resolution::FormalActualMap fa(fn, ci); + auto& badPass = fa.byFormalIdx(candidate.formalIdx()); + const uast::AstNode *actualExpr = nullptr; + const uast::VarLikeDecl *actualDecl = nullptr; + CHPL_ASSERT(0 <= badPass.actualIdx() && + (size_t)badPass.actualIdx() < actualAsts.size()); + actualExpr = actualAsts[badPass.actualIdx()]; + + // look for a definition point of the actual for error reporting of + // uninitialized vars typically in the case of bad split-initialization + if (actualExpr && actualExpr->isIdentifier()) { + auto &resolvedExpr = byPostorder.byAst(actualExpr); + if (auto id = resolvedExpr.toId()) { + auto var = parsing::idToAst(context, id); + // should put a nullptr if not a VarLikeDecl + actualDecl = var->toVarLikeDecl(); } - actualDecls[i] = actualDecl; } + actualDecls[i] = actualDecl; } CHPL_ASSERT(rejected.size() == actualDecls.size()); CHPL_REPORT(context, NoMatchingCandidates, call, ci, rejected, actualDecls); @@ -2458,7 +2459,7 @@ bool Resolver::resolveSpecialNewCall(const Call* call) { // note: the resolution machinery will get compiler generated candidates auto crr = resolveGeneratedCall(context, call, ci, inScopes); - optional action = { { AssociatedAction::NEW_INIT, call->id() } }; + optional action({ AssociatedAction::NEW_INIT, call->id() }); handleResolvedCallPrintCandidates(re, call, ci, inScopes, QualifiedType(), crr, actualAsts, action); diff --git a/frontend/lib/resolution/resolution-error-classes-list.cpp b/frontend/lib/resolution/resolution-error-classes-list.cpp index 3e31d849fa7d..bbac9a190365 100644 --- a/frontend/lib/resolution/resolution-error-classes-list.cpp +++ b/frontend/lib/resolution/resolution-error-classes-list.cpp @@ -685,7 +685,7 @@ static void printRejectedCandidates(ErrorWriterBase& wr, auto actualName = "'" + actualExpr->toIdentifier()->name().str() + "'"; wr.message("The actual ", actualName, " expects to be split-initialized because it is declared without a type or initialization expression here:"); - wr.code(offendingActual, { offendingActual }); + wr.codeForDef(offendingActual); wr.message("The call to '", ci.name() ,"' occurs before any valid initialization points:"); wr.code(actualExpr, { actualExpr }); actualPrinted = true; From 10f836c91f583cb83d8704871ce25eb57b8cd67a Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Mon, 27 Jan 2025 14:33:05 -0700 Subject: [PATCH 14/16] fixes for failing build/dyno tests Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 9 +++++---- .../lib/resolution/resolution-error-classes-list.cpp | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 1227f94c9583..e96c1b62243f 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -1002,16 +1002,17 @@ handleRejectedCandidates(Context* context, if (/* skip computing the formal-actual map because it will go poorly with an unknown formal. */ !candidate.failedDueToWrongActual()) { - continue; + continue; } auto fn = candidate.initialForErr(); resolution::FormalActualMap fa(fn, ci); auto& badPass = fa.byFormalIdx(candidate.formalIdx()); const uast::AstNode *actualExpr = nullptr; const uast::VarLikeDecl *actualDecl = nullptr; - CHPL_ASSERT(0 <= badPass.actualIdx() && - (size_t)badPass.actualIdx() < actualAsts.size()); - actualExpr = actualAsts[badPass.actualIdx()]; + size_t actualIdx = badPass.actualIdx(); + if (0 <= actualIdx && actualIdx < actualAsts.size()) { + actualExpr = actualAsts[badPass.actualIdx()]; + } // look for a definition point of the actual for error reporting of // uninitialized vars typically in the case of bad split-initialization diff --git a/frontend/lib/resolution/resolution-error-classes-list.cpp b/frontend/lib/resolution/resolution-error-classes-list.cpp index bbac9a190365..964a22b25024 100644 --- a/frontend/lib/resolution/resolution-error-classes-list.cpp +++ b/frontend/lib/resolution/resolution-error-classes-list.cpp @@ -651,6 +651,7 @@ static void printRejectedCandidates(ErrorWriterBase& wr, for (auto& candidate : rejected) { if (printCount == maxPrintCount) break; wr.message(""); + auto reason = candidate.reason(); if (/* skip printing detailed info_ here because computing the formal-actual map will go poorly with an unknown formal. */ candidate.failedDueToWrongActual()) { @@ -702,7 +703,6 @@ static void printRejectedCandidates(ErrorWriterBase& wr, if (!actualPrinted && actualExpr) { wr.code(actualExpr, { actualExpr }); } - auto formalReason = candidate.formalReason(); if (formalReason == resolution::FAIL_INCOMPATIBLE_NILABILITY) { auto formalDec = badPass.formalType().type()->toClassType()->decorator(); @@ -761,7 +761,6 @@ static void printRejectedCandidates(ErrorWriterBase& wr, } wr.code(candidate.idForErr()); } else { - auto reason = candidate.reason(); std::string reasonStr = ""; if (reason == resolution::FAIL_VARARG_MISMATCH) { reasonStr = "the number of varargs was incorrect:"; From a2bcbffd995e9427b2365bad96ff75befcf06ea4 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Tue, 28 Jan 2025 11:25:42 -0700 Subject: [PATCH 15/16] add sanity check asserting vectors same size Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index e96c1b62243f..9eb7eba3abba 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -1947,6 +1947,7 @@ Resolver::issueErrorForFailedCallResolution(const uast::AstNode* astForErr, std::vector uninitializedActuals; // could not find a most specific candidate std::vector rejected; + CHPL_ASSERT(rejected.size() == uninitializedActuals.size()); CHPL_REPORT(context, NoMatchingCandidates, astForErr, ci, rejected, uninitializedActuals); } } else { From 37ef570709997c07e21eb5e55ae228bdaa139dd4 Mon Sep 17 00:00:00 2001 From: Ahmad Rezaii Date: Tue, 28 Jan 2025 15:18:15 -0700 Subject: [PATCH 16/16] replace conditional with assert, ensure alignment of actuals and ci.numactuals Signed-off-by: Ahmad Rezaii --- frontend/lib/resolution/Resolver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 9eb7eba3abba..e7120336fc5f 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -1010,9 +1010,8 @@ handleRejectedCandidates(Context* context, const uast::AstNode *actualExpr = nullptr; const uast::VarLikeDecl *actualDecl = nullptr; size_t actualIdx = badPass.actualIdx(); - if (0 <= actualIdx && actualIdx < actualAsts.size()) { - actualExpr = actualAsts[badPass.actualIdx()]; - } + CHPL_ASSERT(0 <= actualIdx && actualIdx < actualAsts.size()); + actualExpr = actualAsts[badPass.actualIdx()]; // look for a definition point of the actual for error reporting of // uninitialized vars typically in the case of bad split-initialization @@ -2445,7 +2444,7 @@ bool Resolver::resolveSpecialNewCall(const Call* call) { // Prepare receiver. auto receiverInfo = CallInfoActual(calledType, USTR("this")); actuals.push_back(std::move(receiverInfo)); - + actualAsts.push_back(newExpr->typeExpression()); // Remaining actuals. prepareCallInfoActuals(call, actuals, questionArg, &actualAsts); CHPL_ASSERT(!questionArg); @@ -2455,6 +2454,7 @@ bool Resolver::resolveSpecialNewCall(const Call* call) { /* hasQuestionArg */ questionArg != nullptr, /* isParenless */ false, std::move(actuals)); + CHPL_ASSERT(actualAsts.size() == (size_t)ci.numActuals()); auto inScope = scopeStack.back(); auto inPoiScope = poiScope; auto inScopes = CallScopeInfo::forNormalCall(inScope, inPoiScope);