-
Notifications
You must be signed in to change notification settings - Fork 413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dyno: Initial resolution of zip()
expressions and parallel iterators
#24915
base: main
Are you sure you want to change the base?
dyno: Initial resolution of zip()
expressions and parallel iterators
#24915
Conversation
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
I'll fix the CI check failures tomorrow, have to head out for the night. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm halfway through, a few comments, mostly minor changes within individual lines.
/** Given an enum type 'et', get a map from the name of each constant | ||
in 'et' to each constant represented as a param value. | ||
If there are multiple enum constants with the same name (which | ||
means the AST is semantically incorrect), then only the first | ||
constant is added to the map. */ | ||
static const std::map<UniqueString, QualifiedType>* | ||
getParamConstantsMapOrNull(Context* context, const EnumType* et); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before I read the implementation, my question is: why would it be nullable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answering my own question, probably if we built without the standard modules or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just like the property of returning nullptr
if the AST for the thing could not be found.
frontend/lib/resolution/Resolver.cpp
Outdated
if (!p || !p->isIndexableLoop()) { | ||
context->error(zip, "zip expressions may only appear as a loop's iterand"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can make this an assertion because we simply can't parse zip
any other way.
frontend/lib/resolution/Resolver.cpp
Outdated
auto actual = zip->actual(i); | ||
|
||
if (actual->isZip()) { | ||
context->error(actual, "nested 'zip' expressions are not supported"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto here. According to the grammar, 'zip' can only occur after foreach
or in
(see zippered_iterator
in chpl.ypp
). So you can remove this check.
frontend/lib/resolution/Resolver.cpp
Outdated
// Try to resolve the parallel leader using the first actual. | ||
if (isFirstActual && prefersParallel) { | ||
const bool emitErrors = requiresParallel; | ||
leaderType = resolveParallelLeaderIterType(*this, actual, actual, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd rather have resolveParallelLeaderIterType
return a boolean for whether or not the call resolution works, and separately check leaderType
for errors. The reason for that is the following: if the user has a leader iterator that matches, but we can't infer its return type. The user also has follower iterators that work, and a serial iterator that's specifically disabled via compilerError
or something. Since we know there exists a leader iterator, we should skip resolving the serial loops (via the "RFINOS" principle, or "resolution error is not overload selection"). On the other hand, depending on whether we could find a valid leader iterator, we should try or give up on resolving followers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to change the structure of the underlying implementation so that the resolved iterator can be gotten through an optional out formal. Then you can see inspect it to see the details you're describing.
frontend/lib/resolution/Resolver.cpp
Outdated
MSC.numBest() == 1 && | ||
MSC.only().fn()->untyped()->kind() == uast::Function::Kind::ITER; | ||
auto iterKindFormal = getIterKindConstantOrUnknown(rv, iterKindStr); | ||
bool needFollower = iterKindStr ? !strcmp(iterKindStr, "follower") : false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tells me you should use std::string
or UniqueString
. Probably the latter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's wrong with a strcmp?! Lol, back in my day...
frontend/lib/resolution/Resolver.cpp
Outdated
if (iterand->isCall()) { | ||
auto runResult = context->runAndTrackErrors([&](Context* context) { | ||
iterand->traverse(rv); | ||
return true; | ||
}); | ||
std::swap(runResult.errors(), traversalErrors); | ||
} else { | ||
iterand->traverse(rv); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why "is call" is the line between speculative and non-speculative resolution -- would you mind giving your rationale?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My rationale is, what "call-like" things will actually resolve to an iterator call? I am not even sure if we allow parenless methods to resolve to iterator calls anymore (if it does then sure we can always introspect).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, what if it's a call-like thing that returns, say, an array? like getMyArray()
? I think it being a call expression is not a particularly useful metric. I suspect we never want to silence errors except when we're trying to invoke the 'leader' iterator on a value, which i believe is handled below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I can go ahead and just always run the speculation for now.
frontend/lib/resolution/Resolver.cpp
Outdated
|
||
QualifiedType idxType; | ||
// Inspect the resolution result to determine what should be done next. | ||
ResolvedExpression& iterandRE = rv.byPostorder.byAst(iterand); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider auto&
here, too.
frontend/lib/resolution/Resolver.cpp
Outdated
// Inspect the resolution result to determine what should be done next. | ||
ResolvedExpression& iterandRE = rv.byPostorder.byAst(iterand); | ||
auto& MSC = iterandRE.mostSpecific(); | ||
auto fn = !MSC.isEmpty() && MSC.numBest() == 1 ? MSC.only().fn() : nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can just use MostSpecificCandidate
's operator bool
here and write this as
auto fn = MSC.only() ? MSC.only().fn() : nullptr;
That's because .only()
returns an empty candidate (without runtime overhead if that worries you) if there are more or less than 1 candidates.
if (!isIter && e->type() == NoMatchingCandidates) { | ||
auto nmc = static_cast<ErrorNoMatchingCandidates*>(e.get()); | ||
if (std::get<0>(nmc->info()) == iterand) { | ||
std::swap(noCandidatesError, e); | ||
continue; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woah, a use of error introspection. I love it.
Adding |
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
This PR introduces resolution for
zip()
expressions as well as resolution of parallel iterators forforall
and[]
loops.For
zip()
expressions that appear as the iterand of a serial loop, the strategy is to resolve only serial iterators for each actual in thezip()
.For
zip()
expressions that appear as the iterand of aforall
loop, the strategy is to:For
[]
loops, the strategy is similar, except serial iterators may be used as a substitute for the leader and followers IFF the leader iterator could not be resolved for the first actual. If the leader iterator could be resolved, but e.g., its return type could not, then serial iterators will not be considered as fallbacks.For iterands that are not
zip()
expressions, the "standalone" parallel iterator is preferred for parallel loops before attempting to resolve a leader/follower combo. As withzip()
,forall
loops will emit an error if no form of parallel iterator could be resolved. All other loops will fall back to serial iterators.Thanks to @vasslitvinov for walking me through the semantics of
zip()
and parallel iterator resolution.FUTURE WORK
tag=tag
,followThis=followThis
)Reviewed by @DanilaFe. Thanks!