diff --git a/compiler/include/resolution.h b/compiler/include/resolution.h index b9c5cd677582..1c6af63fb5f0 100644 --- a/compiler/include/resolution.h +++ b/compiler/include/resolution.h @@ -257,7 +257,13 @@ void resolveCallAndCallee(CallExpr* call, bool allowUnresolved = false); Type* resolveDefaultGenericTypeSymExpr(SymExpr* se); Type* resolveTypeAlias(SymExpr* se); -FnSymbol* tryResolveCall(CallExpr* call, bool checkWithin=false); +enum class PoiSearchMode { + NORMAL, + NON_POI_ONLY, + POI_ONLY, +}; + +FnSymbol* tryResolveCall(CallExpr* call, bool checkWithin=false, PoiSearchMode poiMode = PoiSearchMode::NORMAL); void makeRefType(Type* type); // FnSymbol changes diff --git a/compiler/include/visibleFunctions.h b/compiler/include/visibleFunctions.h index 34fff95605f8..bb4d8f14f598 100644 --- a/compiler/include/visibleFunctions.h +++ b/compiler/include/visibleFunctions.h @@ -76,6 +76,7 @@ void getMoreVisibleFunctionsOrMethods(const char* name, void getVisibleFunctions(const char* name, CallExpr* call, Vec& visibleFns); +BlockStmt* getVisibleFnsInstantiationPt(BlockStmt* block); BlockStmt* getVisibilityScope(Expr* expr); BlockStmt* getInstantiationPoint(Expr* expr); diff --git a/compiler/passes/buildDefaultFunctions.cpp b/compiler/passes/buildDefaultFunctions.cpp index 508d2ba15f2c..a07352df7f6f 100644 --- a/compiler/passes/buildDefaultFunctions.cpp +++ b/compiler/passes/buildDefaultFunctions.cpp @@ -1743,7 +1743,6 @@ FnSymbol* buildSerializeFnSymbol(AggregateType* ct, ArgSymbol** filearg) { FnSymbol* fn = new FnSymbol("serialize"); fn->addFlag(FLAG_COMPILER_GENERATED); - fn->addFlag(FLAG_LAST_RESORT); if (ct->isClass() && ct != dtObject) { fn->addFlag(FLAG_OVERRIDE); } else { @@ -1788,7 +1787,6 @@ static FnSymbol* buildDeserializeFnSymbol(AggregateType* ct, ArgSymbol** filearg FnSymbol* fn = new FnSymbol("deserialize"); fn->addFlag(FLAG_COMPILER_GENERATED); - fn->addFlag(FLAG_LAST_RESORT); if (ct->isClass() && ct != dtObject) fn->addFlag(FLAG_OVERRIDE); else diff --git a/compiler/resolution/functionResolution.cpp b/compiler/resolution/functionResolution.cpp index 066e7f1eccee..d898b5c3306c 100644 --- a/compiler/resolution/functionResolution.cpp +++ b/compiler/resolution/functionResolution.cpp @@ -2992,18 +2992,18 @@ static void resolveRefDeserialization(CallExpr* call) { lhsSE->symbol()->type = typeSE->symbol()->getRefType(); } -static FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState); +static FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState, PoiSearchMode poiMode=PoiSearchMode::NORMAL); FnSymbol* resolveNormalCall(CallExpr* call) { return resolveNormalCall(call, CHECK_NORMAL_CALL); } -FnSymbol* tryResolveCall(CallExpr* call, bool checkWithin) { +FnSymbol* tryResolveCall(CallExpr* call, bool checkWithin, PoiSearchMode poiMode) { check_state_t checkState = CHECK_CALLABLE_ONLY; if (checkWithin) checkState = CHECK_BODY_RESOLVES; - return resolveNormalCall(call, checkState); + return resolveNormalCall(call, checkState, poiMode); } static Type* resolveGenericActual(SymExpr* se, CallExpr* inCall, @@ -3526,16 +3526,60 @@ static void resolveCoerceCopyMove(CallExpr* call) { * * ************************************** | *************************************/ +// +// We gather a list of last-resort candidates as we go. +// The last-resort candidates visible from the call are followed by a NULL +// to separate them from those visible from the point of instantiation. +// +using LastResortCandidates = std::vector; + static bool isGenericRecordInit(CallExpr* call); -static FnSymbol* resolveNormalCall(CallInfo& info, check_state_t checkState); -static FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState); +static FnSymbol* resolveNormalCall(CallInfo& info, check_state_t checkState, PoiSearchMode poiMode = PoiSearchMode::NORMAL); +static FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState, PoiSearchMode poiMode); -static BlockStmt* findVisibleFunctionsAndCandidates( - CallInfo& info, - VisibilityInfo& visInfo, - Vec& visibleFns, - Vec& candidates); +// State for candidate search. Tracks seen, most applicable, etc. candidates, +// and other information like the current POI scope. +struct CandidateSearchState { + CallInfo& info; + VisibilityInfo visInfo; // note: contains state as to the current POI scope. + PtrSet visited; + BlockStmt* scopeUsed = nullptr; // scope last searched for candidates + + // Keep *all* discovered functions in 'visibleFns' and 'mostApplicable' + // so that we can revisit them for error reporting. The lists ( + // visible -> most applicable -> candidates) trickle down into the next. + // All of them only every grow, with numVisitedVis and numVisitedMA tracking + // the point up all candiddates have been moved to the successive list if + // they needed to be. The flow is as follows: + // 1. In each potential scope (call and POI(s)), visible functions get + // placed into `visibleFns`. + // 2. From those, we find the most applicable functions (coarse, early + // filtering, which removes functions on unrelated objects as best as + // I can tell) and move them into `mostApplicable`. + // 3. From those, we either move candidates into the last resort group + // (if they are last resort candidates) or into `candidates`. + // In this way, 'numVisited*' keeps track of where we left off with the + // previous POI / scope to avoid revisiting those functions for the next POI. + int numVisitedVis = 0, numVisitedMA = 0; + Vec visibleFns; + Vec mostApplicable; + Vec candidates; + LastResortCandidates lrc; + + CandidateSearchState(CallInfo& info) + : info(info), visInfo(info) { + visInfo.currStart = getVisibilityScope(info.call); + INT_ASSERT(visInfo.poiDepth == -1); // we have not used it + } + + bool tryFindVisibileCandidatesForExplicitFn(); + void searchOnePoiLevel(); + void skipOnePoiLevel(); + void findVisibleFunctionsAndCandidates(); + void considerLastResortCandidates(); + void explainGatherCandidate(); +}; static int disambiguateByMatch(CallInfo& info, BlockStmt* searchScope, @@ -3991,7 +4035,7 @@ static void maybeWarnGenericActuals(CallExpr* call) { } static -FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState) { +FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState, PoiSearchMode poiMode) { CallInfo info; FnSymbol* retval = NULL; @@ -4017,7 +4061,7 @@ FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState) { if (isTypeConstructionCall(call)) { resolveTypeSpecifier(info); } else { - retval = resolveNormalCall(info, checkState); + retval = resolveNormalCall(info, checkState, poiMode); } } else if (checkState != CHECK_NORMAL_CALL) { @@ -4255,47 +4299,104 @@ static bool overloadSetsOK(CallExpr* call, } -static FnSymbol* resolveForwardedCall(CallInfo& info, check_state_t checkState); +static FnSymbol* resolveForwardedCall(CallInfo& info, check_state_t checkState, PoiSearchMode poiMode = PoiSearchMode::NORMAL); static bool typeUsesForwarding(Type* t); -static FnSymbol* resolveNormalCall(CallInfo& info, check_state_t checkState) { - Vec mostApplicable; - Vec candidates; - +static FnSymbol* resolveNormalCall(CallInfo& info, check_state_t checkState, PoiSearchMode poiMode) { ResolutionCandidate* bestRef = NULL; ResolutionCandidate* bestCref = NULL; ResolutionCandidate* bestVal = NULL; - VisibilityInfo visInfo(info); int numMatches = 0; FnSymbol* retval = NULL; - BlockStmt* scopeUsed = nullptr; + CandidateSearchState searchState(info); + auto& visInfo = searchState.visInfo; + auto& mostApplicable = searchState.mostApplicable; + auto& candidates = searchState.candidates; + auto& scopeUsed = searchState.scopeUsed; + + bool considerNonPoi = (poiMode != PoiSearchMode::POI_ONLY); + bool considerPoi = (poiMode != PoiSearchMode::NON_POI_ONLY); + + if (auto use = toUnresolvedSymExpr(info.call->baseExpr)) { + if (strcmp(use->unresolved, "foo") == 0) debuggerBreakHere(); + } + + if (searchState.tryFindVisibileCandidatesForExplicitFn()) { + /* the function was explicitly specified via FnSymbol*. Don't search + for others and don't consider POI */ + poiMode = PoiSearchMode::NON_POI_ONLY; + } else if (!considerNonPoi) { + /* This function was previously used to search for non-POI, and now, it's + being used to search for only POI. Skip past the non-POI scope. */ + searchState.skipOnePoiLevel(); + } else { + /* At this point, the top-level POI level is the regular scope of the call + (so it's not really POI). This was configured as part of searchState's + constructor. So, this brach is the non-POI candidate search, which + always happens. */ + searchState.searchOnePoiLevel(); + } + + // If no non-POI candidates were found and it's a method, try forwarding. + // Forwarded methods are treated as if they were defined directly on the type, + // so they take precedence over POI candidates. This is crucial for correctness. + // + // See https://github.com/chapel-lang/chapel/issues/28246 + bool forwardingEligible = + candidates.n == 0 && + info.call->numActuals() >= 1 && + info.call->get(1)->typeInfo() == dtMethodToken && + isUnresolvedSymExpr(info.call->baseExpr); + Type* receiverType; + bool usesForwarding = false; - scopeUsed = findVisibleFunctionsAndCandidates(info, visInfo, - mostApplicable, candidates); + // While searching for forwarding candidates, do not consider _their_ POI. + // That's because we haven't looked at the POI candidates for the original type, + // and returning the impl type's POI candidates here would be strange. + if (forwardingEligible) { + receiverType = canonicalDecoratedClassType(info.call->get(2)->getValType()); + if ((usesForwarding = typeUsesForwarding(receiverType))) { + if (considerNonPoi) { + if (auto fn = resolveForwardedCall(info, checkState, PoiSearchMode::NON_POI_ONLY)) { + return fn; + } + } + } + } + + // At this point, we have found no non-POI candidates, neither in the + // original type nor in any fields it forwards. Time to move on to POI. + if (considerPoi) { + // Ok, no forwaring candidates found without POI. Now move on to + // our POI candidates. + if (candidates.n == 0 && visInfo.currStart != nullptr && scopeUsed != visInfo.currStart) { + searchState.findVisibleFunctionsAndCandidates(); + } + + // If we have not found any candidates after traversing all POIs, + // look at "last resort" candidates, if any. + if (candidates.n == 0) { + searchState.considerLastResortCandidates(); + } + } + searchState.explainGatherCandidate(); numMatches = disambiguateByMatch(info, scopeUsed, candidates, bestRef, bestCref, bestVal); - if (checkState == CHECK_NORMAL_CALL && numMatches > 0 && visInfo.inPOI()) + if (numMatches > 0 && visInfo.inPOI()) updateCacheInfosForACall(visInfo, bestRef, bestCref, bestVal); - // If no candidates were found and it's a method, try forwarding - if (candidates.n == 0 && - info.call->numActuals() >= 1 && - info.call->get(1)->typeInfo() == dtMethodToken && - isUnresolvedSymExpr(info.call->baseExpr)) { - Type* receiverType = canonicalDecoratedClassType(info.call->get(2)->getValType()); - if (typeUsesForwarding(receiverType)) { - FnSymbol* fn = resolveForwardedCall(info, checkState); - if (fn) { - return fn; - } - // otherwise error is printed below + // Now, try forwarding again, this time considering POI + if (candidates.n == 0 && forwardingEligible && usesForwarding && considerPoi) { + if (auto fn = resolveForwardedCall(info, checkState, PoiSearchMode::POI_ONLY)) { + return fn; } + // otherwise error is printed below } if (numMatches > 0) { @@ -5367,13 +5468,6 @@ static void generateUnresolvedMsg(CallInfo& info, Vec& visibleFns) { * * ************************************** | *************************************/ -// -// We gather a list of last-resort candidates as we go. -// The last-resort candidates visible from the call are followed by a NULL -// to separate them from those visible from the point of instantiation. -// -typedef std::vector LastResortCandidates; - // add a null separator static void markEndOfPOI(LastResortCandidates& lrc) { lrc.push_back(NULL); @@ -5513,17 +5607,11 @@ void advanceCurrStart(VisibilityInfo& visInfo) { visInfo.nextPOI = NULL; } -// Returns the POI scope used to find the candidates -static BlockStmt* findVisibleFunctionsAndCandidates( - CallInfo& info, - VisibilityInfo& visInfo, - Vec& mostApplicable, - Vec& candidates) { +bool CandidateSearchState::tryFindVisibileCandidatesForExplicitFn() { CallExpr* call = info.call; FnSymbol* fn = call->resolvedFunction(); Vec visibleFns; - - if (fn != NULL) { + if (fn != nullptr) { visibleFns.add(fn); mostApplicable.add(fn); // for better error reporting @@ -5532,41 +5620,54 @@ static BlockStmt* findVisibleFunctionsAndCandidates( // no need for trimVisibleCandidates() and findVisibleCandidates() gatherCandidates(info, visInfo, fn, candidates); - explainGatherCandidate(info, candidates); - - return getVisibilityScope(call); + scopeUsed = getVisibilityScope(call); + return true; } + return false; +} - // CG TODO: pull all visible interface functions, if within a CG context +// when looking for function candidates, we first check the current scope, +// then walk through the POI scopes. For each scope, we execute searchOnePoiLevel(), +// which checks for visible functions and gathers candidates. We stop if +// we find any candidates. +void CandidateSearchState::searchOnePoiLevel() { + // CG TODO: no POI for CG functions + visInfo.poiDepth++; - // Keep *all* discovered functions in 'visibleFns' and 'mostApplicable' - // so that we can revisit them for error reporting. - // Keep track in 'numVisited*' of where we left off with the previous POI - // to avoid revisiting those functions for the next POI. - int numVisitedVis = 0, numVisitedMA = 0; - LastResortCandidates lrc; - PtrSet visited; - visInfo.currStart = getVisibilityScope(call); - INT_ASSERT(visInfo.poiDepth == -1); // we have not used it - BlockStmt* scopeUsed = nullptr; + findVisibleFunctions(info, &visInfo, &visited, + &numVisitedVis, visibleFns); - do { - // CG TODO: no POI for CG functions - visInfo.poiDepth++; + trimVisibleCandidates(info, mostApplicable, + numVisitedVis, visibleFns); + + gatherCandidatesAndLastResort(info, visInfo, mostApplicable, numVisitedMA, + lrc, candidates); + + // save the scope used for disambiguation + scopeUsed = visInfo.currStart; + + advanceCurrStart(visInfo); +} - findVisibleFunctions(info, &visInfo, &visited, - &numVisitedVis, visibleFns); +void CandidateSearchState::skipOnePoiLevel() { + visInfo.poiDepth++; + scopeUsed = visInfo.currStart; + visInfo.nextPOI = getVisibleFnsInstantiationPt(scopeUsed); + visInfo.visitedScopes.push_back(scopeUsed); + visited.insert(scopeUsed); + advanceCurrStart(visInfo); +} - trimVisibleCandidates(info, mostApplicable, - numVisitedVis, visibleFns); +// Returns the POI scope used to find the candidates +void CandidateSearchState::findVisibleFunctionsAndCandidates() { + // CG TODO: pull all visible interface functions, if within a CG context - gatherCandidatesAndLastResort(info, visInfo, mostApplicable, numVisitedMA, - lrc, candidates); + BlockStmt* scopeUsed = nullptr; - // save the scope used for disambiguation - scopeUsed = visInfo.currStart; + do { + // CG TODO: no POI for CG functions - advanceCurrStart(visInfo); + searchOnePoiLevel(); // prevent infinite loop if (scopeUsed == visInfo.currStart) { @@ -5575,9 +5676,9 @@ static BlockStmt* findVisibleFunctionsAndCandidates( } while (candidates.n == 0 && visInfo.currStart != NULL); +} - // If we have not found any candidates after traversing all POIs, - // look at "last resort" candidates, if any. +void CandidateSearchState::considerLastResortCandidates() { if (candidates.n == 0 && haveAnyLRCs(lrc, visInfo.poiDepth)) { visInfo.poiDepth = -1; int numVisitedLRC = 0; @@ -5588,10 +5689,10 @@ static BlockStmt* findVisibleFunctionsAndCandidates( while (candidates.n == 0 && haveMoreLRCs(lrc, numVisitedLRC)); } +} - explainGatherCandidate(info, candidates); - - return scopeUsed; +void CandidateSearchState::explainGatherCandidate() { + ::explainGatherCandidate(info, candidates); } // run filterCandidate() on 'fn' if appropriate @@ -5738,7 +5839,7 @@ static const char* getForwardedMethodName(const char* calledName, return methodName; } -static FnSymbol* adjustAndResolveForwardedCall(CallExpr* call, ForwardingStmt* delegate, const char* methodName) { +static FnSymbol* adjustAndResolveForwardedCall(CallExpr* call, ForwardingStmt* delegate, const char* methodName, PoiSearchMode poiMode) { FnSymbol* ret = NULL; const char* fnGetTgt = delegate->fnReturningForwarding; @@ -5780,7 +5881,7 @@ static FnSymbol* adjustAndResolveForwardedCall(CallExpr* call, ForwardingStmt* d } resolveCall(setTgt); - ret = tryResolveCall(call); + ret = tryResolveCall(call, /* checkWithin */ false, poiMode); } return ret; @@ -5789,7 +5890,7 @@ static FnSymbol* adjustAndResolveForwardedCall(CallExpr* call, ForwardingStmt* d llvm::SmallVector, 4> forwardCallCycleSet; // Returns a relevant FnSymbol if it worked -static FnSymbol* resolveForwardedCall(CallInfo& info, check_state_t checkState) { +static FnSymbol* resolveForwardedCall(CallInfo& info, check_state_t checkState, PoiSearchMode poiMode) { CallExpr* call = info.call; const char* calledName = astr(info.name); const char* inFnName = astr(call->getFunction()->name); @@ -5871,7 +5972,7 @@ static FnSymbol* resolveForwardedCall(CallInfo& info, check_state_t checkState) BlockStmt* block = new BlockStmt(forwardedCall, BLOCK_SCOPELESS); call->getStmtExpr()->insertBefore(block); - auto fn = adjustAndResolveForwardedCall(forwardedCall, delegate, methodName); + auto fn = adjustAndResolveForwardedCall(forwardedCall, delegate, methodName, poiMode); if (fn) { if (bestFn == NULL) { bestFn = fn; @@ -5903,7 +6004,7 @@ static FnSymbol* resolveForwardedCall(CallInfo& info, check_state_t checkState) const char* methodName = getForwardedMethodName(calledName, bestDelegate); INT_ASSERT(methodName); - bestFn = adjustAndResolveForwardedCall(call, bestDelegate, methodName); + bestFn = adjustAndResolveForwardedCall(call, bestDelegate, methodName, poiMode); } else { // Replace actuals in call with those from bestCall // Note that the above path could be used instead, but diff --git a/compiler/resolution/visibleFunctions.cpp b/compiler/resolution/visibleFunctions.cpp index e4c22fd9567b..2da24ffb6511 100644 --- a/compiler/resolution/visibleFunctions.cpp +++ b/compiler/resolution/visibleFunctions.cpp @@ -411,8 +411,6 @@ static void lookAtTypeFirst(const char* name, CallExpr* call, BlockStmt* block, PtrSet& visited, Vec& visibleFns); -static BlockStmt* getVisibleFnsInstantiationPt(BlockStmt* block); - static void getVisibleFnsShowBlock(const char* context, BlockStmt* block, BlockStmt* instantiationPt); @@ -1039,7 +1037,7 @@ void getVisibleFunctions(const char* name, visited, visibleFns, false); } -static BlockStmt* getVisibleFnsInstantiationPt(BlockStmt* block) { +BlockStmt* getVisibleFnsInstantiationPt(BlockStmt* block) { BlockStmt* instantiationPt = NULL; // We check for an instantiation point only at FnSymbols' diff --git a/test/classes/forwarding/forwarding-poi-order.1-0.good b/test/classes/forwarding/forwarding-poi-order.1-0.good new file mode 100644 index 000000000000..bda709ecfb47 --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.1-0.good @@ -0,0 +1,3 @@ +42 +42 +42 diff --git a/test/classes/forwarding/forwarding-poi-order.2-0.good b/test/classes/forwarding/forwarding-poi-order.2-0.good new file mode 100644 index 000000000000..daaac9e30302 --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.2-0.good @@ -0,0 +1,2 @@ +42 +42 diff --git a/test/classes/forwarding/forwarding-poi-order.3-0.good b/test/classes/forwarding/forwarding-poi-order.3-0.good new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.3-0.good @@ -0,0 +1 @@ +42 diff --git a/test/classes/forwarding/forwarding-poi-order.4.good b/test/classes/forwarding/forwarding-poi-order.4.good new file mode 100644 index 000000000000..29b4e22ea105 --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.4.good @@ -0,0 +1,4 @@ +forwarding-poi-order.chpl:34: In function 'generic': +forwarding-poi-order.chpl:35: error: unresolved call 'Outermost.foo()' +forwarding-poi-order.chpl:35: note: because no functions named foo found in scope + forwarding-poi-order.chpl:40: called as generic(x: Outermost) diff --git a/test/classes/forwarding/forwarding-poi-order.5.good b/test/classes/forwarding/forwarding-poi-order.5.good new file mode 100644 index 000000000000..30cc8d66d57b --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.5.good @@ -0,0 +1,4 @@ +forwarding-poi-order.chpl:34: In function 'generic': +forwarding-poi-order.chpl:35: error: unresolved call 'Outermost.foo()' +forwarding-poi-order.chpl:35: note: because no functions named foo found in scope + forwarding-poi-order.chpl:53: called as generic(x: Outermost) diff --git a/test/classes/forwarding/forwarding-poi-order.6.good b/test/classes/forwarding/forwarding-poi-order.6.good new file mode 100644 index 000000000000..30cc8d66d57b --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.6.good @@ -0,0 +1,4 @@ +forwarding-poi-order.chpl:34: In function 'generic': +forwarding-poi-order.chpl:35: error: unresolved call 'Outermost.foo()' +forwarding-poi-order.chpl:35: note: because no functions named foo found in scope + forwarding-poi-order.chpl:53: called as generic(x: Outermost) diff --git a/test/classes/forwarding/forwarding-poi-order.chpl b/test/classes/forwarding/forwarding-poi-order.chpl new file mode 100644 index 000000000000..cb37674ecff3 --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.chpl @@ -0,0 +1,62 @@ +module A { + config param enableInnerFoo = true; + + record Inner { + proc foo() where enableInnerFoo { + return 42; + } + } +} + +module B { + import A.Inner; + + record Outer { + forwarding var inner: Inner; + } +} + +module C { + import B.Outer; + + record Outermost { + forwarding var outer: Outer; + } +} + +module D { + import A.Inner; + import B.Outer; + import C.Outermost; + + config param startAtCase = 1; + + proc generic(x) { + writeln(x.foo()); + } + + proc case1() { + if startAtCase <= 1 { + generic(new Outermost()); + } + } + + proc case2() { + if startAtCase <= 2 { + proc Outer.foo() do return "Outer foo"; + generic(new Outermost()); + } + } + + proc case3() { + if startAtCase <= 3 { + generic(new Outermost()); + } + } + + proc main() { + case1(); + case2(); + case3(); + } +} diff --git a/test/classes/forwarding/forwarding-poi-order.compopts b/test/classes/forwarding/forwarding-poi-order.compopts new file mode 100644 index 000000000000..04cc48ed74c9 --- /dev/null +++ b/test/classes/forwarding/forwarding-poi-order.compopts @@ -0,0 +1,6 @@ +-sstartAtCase=1 +-sstartAtCase=2 +-sstartAtCase=3 +-sstartAtCase=1 -senableInnerFoo=false +-sstartAtCase=2 -senableInnerFoo=false +-sstartAtCase=3 -senableInnerFoo=false diff --git a/test/library/standard/Reflection/speculative-poi-cache.chpl b/test/library/standard/Reflection/speculative-poi-cache.chpl new file mode 100644 index 000000000000..d25323ce6b83 --- /dev/null +++ b/test/library/standard/Reflection/speculative-poi-cache.chpl @@ -0,0 +1,20 @@ +record Outermost {} + +proc generic(x) { + use Reflection; + return canResolveMethod(x, "foo"); +} + +proc case1() { + proc Outermost.foo() do return "Outer foo"; + return generic(new Outermost()); +} + +proc case2() { + return generic(new Outermost()); +} + +proc main() { + writeln(case1()); + writeln(case2()); +} diff --git a/test/library/standard/Reflection/speculative-poi-cache.good b/test/library/standard/Reflection/speculative-poi-cache.good new file mode 100644 index 000000000000..da29283aaa47 --- /dev/null +++ b/test/library/standard/Reflection/speculative-poi-cache.good @@ -0,0 +1,2 @@ +true +false