Skip to content

Get dyno resolving array slicing #27024

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

Merged
merged 12 commits into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion frontend/lib/resolution/InitResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,11 @@ static const ArrayType* arrayTypeFromSubsHelper(
} else if (instanceBct->id().symbolPath() ==
"DefaultAssociative.DefaultAssociativeArr" ||
instanceBct->id().symbolPath() ==
"ArrayViewReindex.ArrayViewReindexArr") {
"ArrayViewReindex.ArrayViewReindexArr" ||
instanceBct->id().symbolPath() ==
"ArrayViewSlice.ArrayViewSliceArr" ||
instanceBct->id().symbolPath() ==
"ArrayViewRankChange.ArrayViewRankChangeArr") {
auto [domInstanceQt] = extractFields(rc, instanceBct, "dom");
auto domain = domainTypeFromInstance(rc, domInstanceQt);
CHPL_ASSERT(domain);
Expand Down
26 changes: 18 additions & 8 deletions frontend/lib/resolution/Resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5649,6 +5649,10 @@ QualifiedType Resolver::typeForEnumElement(const EnumType* enumType,
return qt;
}

static bool isDotDomainAccess(const Dot* dot) {
return dot->field() == USTR("domain");
}

void Resolver::exit(const Dot* dot) {
ResolvedExpression& receiver = byPostorder.byAst(dot->receiver());
ResolvedExpression& r = byPostorder.byAst(dot);
Expand All @@ -5665,9 +5669,11 @@ void Resolver::exit(const Dot* dot) {

// Try to resolve a it as a field/parenless proc so we can resolve 'this' on
// it later if needed.
// Special case: Don't try to resolve calls to .domain here, as we
// need to proceed to the handling logic below.
if (!receiver.type().isUnknown() && receiver.type().type() &&
receiver.type().type()->getCompositeType() &&
dot->field() != "init") {
dot->field() != USTR("init") && !isDotDomainAccess(dot)) {
std::vector<CallInfoActual> actuals;
actuals.push_back(CallInfoActual(receiver.type(), USTR("this")));
auto ci = CallInfo(/* name */ dot->field(),
Expand Down Expand Up @@ -5710,11 +5716,13 @@ void Resolver::exit(const Dot* dot) {
}

// Handle .domain on an array (which doesn't exist in module code) as a call
// to _dom.
if (receiver.type().type() && receiver.type().type()->isArrayType() &&
dot->field() == USTR("domain")) {
// to _dom. We do this even for a non-array receiver as that's what production
// does.
if (isDotDomainAccess(dot)) {
std::vector<CallInfoActual> actuals;
std::vector<const AstNode*> actualAsts;
actuals.emplace_back(receiver.type(), USTR("this"));
actualAsts.push_back(dot->receiver());
auto name = UniqueString::get(context, "_dom");
auto ci = CallInfo(/* name */ name,
/* calledType */ QualifiedType(),
Expand All @@ -5723,10 +5731,12 @@ void Resolver::exit(const Dot* dot) {
/* isParenless */ true, actuals);
auto inScope = currentScope();
auto inScopes = CallScopeInfo::forNormalCall(inScope, poiScope);
auto rr = resolveGeneratedCall(dot, &ci, &inScopes, name.c_str());

auto baseDomainType = rr.result.exprType().type()->toDomainType();
r.setType(QualifiedType(QualifiedType::CONST_VAR, baseDomainType));
if (shouldSkipCallResolution(this, dot, actualAsts, ci)) {
r.setType(QualifiedType());
} else {
auto rr = resolveGeneratedCall(dot, &ci, &inScopes, name.c_str());
rr.noteResultWithoutError(&r);
}
return;
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/resolution/resolution-queries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5175,7 +5175,7 @@ gatherAndFilterCandidates(ResolutionContext* rc,
// The 'isInsideForwarding' check below would prevent resolving a method
// 'bar()' on 'b'.

if (typeUsesForwarding(context, receiverType) &&
if (receiverType && typeUsesForwarding(context, receiverType) &&
!isInsideForwarding(context, call)) {
CandidatesAndForwardingInfo nonPoiCandidates;
CandidatesAndForwardingInfo poiCandidates;
Expand Down
64 changes: 50 additions & 14 deletions frontend/test/resolution/testArrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,8 @@
#include "chpl/uast/Record.h"
#include "chpl/uast/Variable.h"

// TODO:
// - Slices

// Test using an array with a type expression consisting of the given domain
// and element type.
// Optionally accepts arguments to test indexing into the array with (in
// addition to doing so with the default value of the domain's index type).
static void testArray(std::string domainType,
std::string eltType,
std::string testIdxArg = "") {
std::string eltType) {
std::string arrayText;
arrayText += "[" + domainType + "] " + eltType;
printf("Testing array type expression: %s\n", arrayText.c_str());
Expand Down Expand Up @@ -94,8 +86,7 @@ module M {

// indexing
var idx : index(A.domain);
var x1 = A[idx];
var x2 = A[)""" + (testIdxArg.empty() ? "idx" : testIdxArg) + R"""(];
var x = A[idx];

// iteration
for loopI in A {
Expand Down Expand Up @@ -185,8 +176,7 @@ module M {
assert(findVarType(m, rr, "assocIndices").type()->isArrayType());
}

assert(findVarType(m, rr, "x1").type() == eType.type());
assert(findVarType(m, rr, "x2").type() == eType.type());
assert(findVarType(m, rr, "x").type() == eType.type());

assert(findVarType(m, rr, "z").type() == eType.type());

Expand Down Expand Up @@ -254,11 +244,52 @@ static void testArrayLiteral(std::string arrayLiteral, std::string domainType,
assert(guard.realizeErrors() == 0);
}

static void testArraySlicing(std::string arrayLiteral, std::string sliceArgs,
std::string resultType) {
printf("Testing slicing array %s with %s\n", arrayLiteral.c_str(),
sliceArgs.c_str());

Context* context = buildStdContext();
ErrorGuard guard(context);

auto vars = resolveTypesOfVariables(context,
R"""(
module M {
var A = )""" + arrayLiteral + R"""(;
var mySlice = A[)""" + sliceArgs + R"""(];
type gotSliceTy = mySlice.type;
type expectedSliceTy = )""" + resultType + R"""(;
}
)""",
{"A", "gotSliceTy", "expectedSliceTy"});
auto arrType = vars.at("A");
assert(arrType.type());
assert(!arrType.type()->isUnknownType());
assert(arrType.type()->isArrayType());

auto gotSliceTy = vars.at("gotSliceTy");
assert(gotSliceTy.type());
assert(!gotSliceTy.type()->isUnknownType());

auto expectedSliceTy = vars.at("expectedSliceTy");
assert(expectedSliceTy.type());
assert(!expectedSliceTy.type()->isUnknownType());

// Compare types approximately by stringifying, since they won't have the
// same underlying instance
std::stringstream ss1, ss2;
gotSliceTy.type()->stringify(ss1, chpl::StringifyKind::CHPL_SYNTAX);
expectedSliceTy.type()->stringify(ss2, chpl::StringifyKind::CHPL_SYNTAX);
assert(ss1.str() == ss2.str());

assert(guard.realizeErrors() == 0);
}

int main() {
// rectangular
testArray("domain(1)", "int");
testArray("domain(1)", "string");
testArray("domain(2)", "int", "0, 1");
testArray("domain(2)", "int");

// 1D literals
testArrayLiteral("[1, 2, 3]", "domain(1)", "int");
Expand All @@ -274,6 +305,11 @@ int main() {
testArrayLiteral("[1;]", "domain(2)", "int");
testArrayLiteral("[1, 2; 3, 4;; 5, 6; 7, 8]", "domain(3)", "int");

// slices
testArraySlicing("[1, 2, 3]", "0..1", "[0..1] int");
testArraySlicing("[1, 2; 3, 4]", "0, 1", "int"); // testing multi-dim indexing, not really a slice
testArraySlicing("[1, 2; 3, 4;; 5, 6; 7, 8]", "0..1, 0..1, 1", "[0..1, 0..1] int");

// associative
testArray("domain(int)", "int");
testArray("domain(int, true)", "int");
Expand Down