diff --git a/frontend/lib/resolution/InitResolver.cpp b/frontend/lib/resolution/InitResolver.cpp
index 6b860bd910ed..09d6d5550cfd 100644
--- a/frontend/lib/resolution/InitResolver.cpp
+++ b/frontend/lib/resolution/InitResolver.cpp
@@ -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);
diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp
index c63e282d0abb..f56765a68e0d 100644
--- a/frontend/lib/resolution/Resolver.cpp
+++ b/frontend/lib/resolution/Resolver.cpp
@@ -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);
@@ -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 actuals;
actuals.push_back(CallInfoActual(receiver.type(), USTR("this")));
auto ci = CallInfo(/* name */ dot->field(),
@@ -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 actuals;
+ std::vector 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(),
@@ -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;
}
diff --git a/frontend/lib/resolution/resolution-queries.cpp b/frontend/lib/resolution/resolution-queries.cpp
index 644ee2daac71..a3400dfc2066 100644
--- a/frontend/lib/resolution/resolution-queries.cpp
+++ b/frontend/lib/resolution/resolution-queries.cpp
@@ -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;
diff --git a/frontend/test/resolution/testArrays.cpp b/frontend/test/resolution/testArrays.cpp
index 00a78e288708..2ca00d24b3af 100644
--- a/frontend/test/resolution/testArrays.cpp
+++ b/frontend/test/resolution/testArrays.cpp
@@ -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());
@@ -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 {
@@ -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());
@@ -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");
@@ -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");