From 9b502f9c427ae3072ee07614ac771a61488c82f0 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 29 Mar 2024 12:12:44 -0700 Subject: [PATCH 1/2] Improve leaf call handling in the resolver to support chained calls Signed-off-by: Danila Fedorin --- frontend/lib/resolution/Resolver.cpp | 32 ++++++++++++++++++---------- frontend/lib/resolution/Resolver.h | 13 ++++++++++- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index 69cead3dc21e..b7c1abd5daa6 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -394,6 +394,12 @@ Resolver::paramLoopResolver(Resolver& parent, return ret; } +const AstNode* Resolver::nearestCalledExpression() const { + if (callNodeStack.empty()) return nullptr; + + return callNodeStack.back()->calledExpression(); +} + void Resolver::setCompositeType(const CompositeType* ct) { CHPL_ASSERT(this->inCompositeType == nullptr); this->inCompositeType = ct; @@ -2552,8 +2558,7 @@ Resolver::lookupIdentifier(const Identifier* ident, CHPL_ASSERT(scopeStack.size() > 0); const Scope* scope = scopeStack.back(); - bool resolvingCalledIdent = (inLeafCall && - ident == inLeafCall->calledExpression()); + bool resolvingCalledIdent = nearestCalledExpression() == ident; LookupConfig config = LOOKUP_DECLS | LOOKUP_IMPORT_AND_USE | @@ -2842,8 +2847,8 @@ void Resolver::resolveIdentifier(const Identifier* ident, // record R { type t = int; } // var x: R; // should refer to R(int) bool computeDefaults = true; - bool resolvingCalledIdent = (inLeafCall && - ident == inLeafCall->calledExpression()); + bool resolvingCalledIdent = nearestCalledExpression() == ident; + if (resolvingCalledIdent) { computeDefaults = false; } @@ -2962,7 +2967,7 @@ bool Resolver::enter(const TypeQuery* tq) { if (!foundFormalSubstitution) { // No substitution (i.e. initial signature) so use AnyType - if (inLeafCall && isCallToIntEtc(inLeafCall)) { + if (!callNodeStack.empty() && isCallToIntEtc(callNodeStack.back())) { auto defaultInt = IntType::get(context, 0); result.setType(QualifiedType(QualifiedType::PARAM, defaultInt)); } else { @@ -3329,7 +3334,7 @@ types::QualifiedType Resolver::typeForBooleanOp(const uast::OpCall* op) { } bool Resolver::enter(const Call* call) { - inLeafCall = call; + callNodeStack.push_back(call); auto op = call->toOpCall(); if (op && initResolver) { @@ -3371,9 +3376,10 @@ void Resolver::prepareCallInfoActuals(const Call* call, /* actualAsts */ nullptr); } -void Resolver::exit(const Call* call) { - if (scopeResolveOnly) +void Resolver::handleCallExpr(const uast::Call* call) { + if (scopeResolveOnly) { return; + } if (initResolver && initResolver->handleResolvingCall(call)) return; @@ -3484,8 +3490,13 @@ void Resolver::exit(const Call* call) { ResolvedExpression& r = byPostorder.byAst(call); r.setType(QualifiedType()); } +} + +void Resolver::exit(const Call* call) { + handleCallExpr(call); - inLeafCall = nullptr; + // Always remove the call from the stack to make sure it's properly set. + callNodeStack.pop_back(); } bool Resolver::enter(const Dot* dot) { @@ -3565,8 +3576,7 @@ void Resolver::exit(const Dot* dot) { ResolvedExpression& receiver = byPostorder.byAst(dot->receiver()); - bool resolvingCalledDot = (inLeafCall && - dot == inLeafCall->calledExpression()); + bool resolvingCalledDot = nearestCalledExpression() == dot; if (resolvingCalledDot && !scopeResolveOnly) { // We will handle it when resolving the FnCall. diff --git a/frontend/lib/resolution/Resolver.h b/frontend/lib/resolution/Resolver.h index bb0c3e87c27c..0873f3a9084d 100644 --- a/frontend/lib/resolution/Resolver.h +++ b/frontend/lib/resolution/Resolver.h @@ -58,7 +58,7 @@ struct Resolver { std::set fieldOrFormals; std::set instantiatedFieldOrFormals; std::set namesWithErrorsEmitted; - const uast::Call* inLeafCall = nullptr; + std::vector callNodeStack; bool receiverScopesComputed = false; ReceiverScopesVec savedReceiverScopes; Resolver* parentResolver = nullptr; @@ -206,6 +206,12 @@ struct Resolver { const uast::For* loop, ResolutionResultByPostorderID& bodyResults); + /** + During AST traversal, find the last called expression we entered. + e.g., will return 'f' if we just entered 'f()'. + */ + const chpl::uast::AstNode* nearestCalledExpression() const; + // Set the composite type of this Resolver. It is an error to call this // method when a composite type is already set. void setCompositeType(const types::CompositeType* ct); @@ -336,6 +342,11 @@ struct Resolver { ID moduleId, LookupConfig failedConfig); + // after resolving the child nodes of the call as needed, perform call resolution + // if appropriate. This is a helper function because it has some complicated + // control flow, and we want to make sure to always keep callNodeStack in sync. + void handleCallExpr(const uast::Call* call); + // handle the result of one of the functions to resolve a call. Handles: // * r.setMostSpecific // * r.setPoiScope From 0ca9c31d712b1c963165442a36d40a6a6518f785 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 29 Mar 2024 12:26:38 -0700 Subject: [PATCH 2/2] Add examples from Ben's issue to testResolve Signed-off-by: Danila Fedorin --- frontend/test/resolution/testResolve.cpp | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/frontend/test/resolution/testResolve.cpp b/frontend/test/resolution/testResolve.cpp index 91071f65609f..6d9f46d835b2 100644 --- a/frontend/test/resolution/testResolve.cpp +++ b/frontend/test/resolution/testResolve.cpp @@ -1342,6 +1342,72 @@ static void test22() { } } +static void test23() { + Context ctx; + Context* context = &ctx; + ErrorGuard guard(context); + + { + std::string prog = + R"""( + record R { + var x : int; + + proc foo() { + return 5; + } + } + + proc helper() { + var r : R; + return r; + } + + proc foo() { + return "hello"; + } + + // should be an int, not a string. + var x = helper().foo(); + )"""; + + auto t = resolveTypeOfXInit(context, prog); + assert(t.type()); + assert(t.type()->isIntType()); + assert(t.type()->toIntType()->isDefaultWidth()); + } + + { + context->advanceToNextRevision(false); + std::string prog = + R"""( + record Inner { + var x : int; + + proc innerFoo() { + return x; + } + } + + record Outer { + var inner : Inner; + + proc helper() const ref { + return inner; + } + } + + var o : Outer; + var x = o.helper().innerFoo(); + )"""; + + auto t = resolveTypeOfXInit(context, prog); + assert(t.type()); + assert(t.type()->isIntType()); + assert(t.type()->toIntType()->isDefaultWidth()); + } +} + int main() { test1(); test2(); @@ -1365,6 +1431,7 @@ int main() { test20(); test21(); test22(); + test23(); return 0; }