Skip to content

Commit

Permalink
Dyno: Fix method scopes when using methods or fields in signatures (c…
Browse files Browse the repository at this point in the history
…hapel-lang#23665)

This PR fixes various bugs when trying to use a field or method in the
signature of a method on the same type. The
``Resolver::createForInitialSignature`` method is updated to resolve a
``this`` formal in the case of methods, and used the resolved type as
the Resolver's ``inCompositeType`` value.

Add ``Resolver::setCompositeType`` as a means of setting the Resolver's
composite type once the ``this`` formal has been instantiated.

This PR also implements the logical-not operator, ``!``, for param
bools.

[reviewed-by @mppf]
  • Loading branch information
benharsh authored Oct 24, 2023
2 parents 16dcee9 + 6988657 commit f462c76
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
17 changes: 17 additions & 0 deletions frontend/lib/resolution/Resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ Resolver::createForInitialSignature(Context* context, const Function* fn,
ret.signatureOnly = true;
ret.fnBody = fn->body();
ret.byPostorder.setupForSignature(fn);

if (fn->isMethod()) {
fn->thisFormal()->traverse(ret);
auto receiverType = ret.byPostorder.byAst(fn->thisFormal()).type();
if (receiverType.hasTypePtr()) {
if (auto ct = receiverType.type()->toCompositeType()) {
ret.inCompositeType = ct;
}
}
}

return ret;
}

Expand All @@ -154,6 +165,7 @@ Resolver::createForInstantiatedSignature(Context* context,
ret.signatureOnly = true;
ret.fnBody = fn->body();
ret.byPostorder.setupForSignature(fn);

return ret;
}

Expand Down Expand Up @@ -368,6 +380,11 @@ Resolver::paramLoopResolver(Resolver& parent,
return ret;
}

void Resolver::setCompositeType(const CompositeType* ct) {
CHPL_ASSERT(this->inCompositeType == nullptr);
this->inCompositeType = ct;
}

std::vector<types::QualifiedType>
Resolver::getFormalTypes(const Function* fn) {
std::vector<types::QualifiedType> formalTypes;
Expand Down
4 changes: 4 additions & 0 deletions frontend/lib/resolution/Resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ struct Resolver {
const uast::For* loop,
ResolutionResultByPostorderID& bodyResults);

// Set the composite type of this Resolver. It is an error to call this
// method when a composite type is already set.
void setCompositeType(const types::CompositeType* ct);

/* Get the formal types from a Resolver that computed them
*/
std::vector<types::QualifiedType> getFormalTypes(const uast::Function* fn);
Expand Down
15 changes: 15 additions & 0 deletions frontend/lib/resolution/resolution-queries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,10 @@ const TypedFnSignature* instantiateSignature(Context* context,
// Type query constraints were not satisfied
return nullptr;
}

if (fn != nullptr && fn->isMethod() && fn->thisFormal() == formal) {
visitor.setCompositeType(qFormalType.type()->toCompositeType());
}
}

// instantiate the VarArg formal if necessary
Expand Down Expand Up @@ -2807,6 +2811,17 @@ static bool resolveFnCallSpecial(Context* context,
}
}

if (ci.isOpCall() && ci.name() == USTR("!") && ci.numActuals() == 1) {
auto qt = ci.actual(0).type();
if (qt.kind() == QualifiedType::PARAM && qt.hasParamPtr() &&
qt.hasTypePtr() && qt.type()->isBoolType()) {
exprTypeOut = qt.param()->fold(context,
chpl::uast::PrimitiveTag::PRIM_UNARY_LNOT,
qt, QualifiedType());
return true;
}
}

if (ci.name() == USTR("isCoercible")) {
if (ci.numActuals() != 2) {
context->error(astForErr, "bad call to %s", ci.name().c_str());
Expand Down
113 changes: 113 additions & 0 deletions frontend/test/resolution/testMethodCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "chpl/types/all-types.h"
#include "chpl/uast/all-uast.h"

#include <functional>

// Test resolving a simple primary and secondary method in defining scope.
static void test1() {
Context ctx;
Expand Down Expand Up @@ -383,6 +385,116 @@ static void test7() {
}
}

static void runAndAssert(std::string program,
std::function<bool(QualifiedType)> fn) {
Context ctx;
Context* context = &ctx;
ErrorGuard guard(context);
QualifiedType initType = resolveTypeOfXInit(context, program);
//assert(initType.type()->isStringType());
assert(fn(initType));
}

//
// Test method signatures that use fields or methods in the same type.
//
static void test8() {
std::string base =
R"""(
record R {
param flag : bool;
proc paramMethod() param : bool {
return flag;
}
proc withDefaultField(arg = flag) {
return "hello";
}
proc withDefault(arg = paramMethod()) {
return "hello";
}
proc whereMethod() where paramMethod() {
return "hello";
}
proc whereMethod() where !paramMethod() {
return 5;
}
proc onlyFalse() where !paramMethod() {
return 42.0;
}
proc whereField() where flag {
return "hello";
}
proc whereField() where !flag {
return 5;
}
}
)""";

auto isString = [](QualifiedType qt) { return qt.type()->isStringType(); };
auto isInt = [](QualifiedType qt) { return qt.type()->isIntType(); };

// Resolve method using a sibling method as an argument's default
runAndAssert(base + R""""(
var r : R(false);
var x = r.withDefault();
)"""", isString);

// Resolve method using a field as an argument's default value
runAndAssert(base + R""""(
var r : R(false);
var x = r.withDefaultField();
)"""", isString);

// Resolve method using another method as the where-clause condition
runAndAssert(base + R""""(
var r : R(true);
var x = r.whereMethod();
)"""", isString);

runAndAssert(base + R""""(
var r : R(false);
var x = r.whereMethod();
)"""", isInt);

// Resolve method using a field as the where-clause condition
runAndAssert(base + R""""(
var r : R(true);
var x = r.whereField();
)"""", isString);

runAndAssert(base + R""""(
var r : R(false);
var x = r.whereField();
)"""", isInt);

// Ensure that methods whose where-clause always results in 'false' cannot
// be called.
{
Context ctx;
Context* context = &ctx;
ErrorGuard guard(context);

std::string program = base + R""""(
var r : R(true);
var x = r.onlyFalse();
)"""";

QualifiedType initType = resolveTypeOfXInit(context, program);
assert(guard.numErrors() == 1);
assert(initType.type()->isErroneousType());
assert(guard.error(0)->message() == "Cannot resolve call to 'onlyFalse': no matching candidates");
guard.realizeErrors();
}
}


int main() {
test1();
Expand All @@ -392,6 +504,7 @@ int main() {
test5();
test6();
test7();
test8();

return 0;
}
Expand Down

0 comments on commit f462c76

Please sign in to comment.