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");