Skip to content

Commit

Permalink
Fix missing instantiatedFrom on generic-inheriting class types (cha…
Browse files Browse the repository at this point in the history
…pel-lang#25859)

This prevented Dyno from resolving method calls on class types that are
generic only by virtue of inheriting from a generic parent.

Fixed by relaxing the guard on setting `instantiatedFrom` in
`ctFromSubs` to also allow it for class types with a generic parent.
Along with this, relax the assertion in the `CompositeType` constructor
requires no `instantiatedFrom` with empty substitutions, to not be
enforced on class types.

Includes adding test coverage of the fixed case.

Resolves Cray/chapel-private#6672.

[reviewed by @benharsh , thanks!]

Testing:
- [x] dyno tests
  • Loading branch information
riftEmber authored Sep 4, 2024
2 parents 9614577 + f80630e commit 9bf3a88
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 9 deletions.
7 changes: 5 additions & 2 deletions frontend/include/chpl/types/CompositeType.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,11 @@ class CompositeType : public Type {
CHPL_ASSERT(instantiatedFrom_ == nullptr ||
instantiatedFrom_->instantiatedFrom_ == nullptr);

// check that subs is consistent with instantiatedFrom
CHPL_ASSERT((instantiatedFrom_ == nullptr) == subs_.empty());
// check that subs is consistent with instantiatedFrom, except in the
// case of class types which can be generic with empty subs due to
// inheritance
CHPL_ASSERT(tag == typetags::BasicClassType ||
(instantiatedFrom_ == nullptr) == subs_.empty());
}

bool compositeTypeContentsMatchInner(const CompositeType* other) const {
Expand Down
16 changes: 9 additions & 7 deletions frontend/lib/resolution/InitResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,22 +270,23 @@ static const Type* ctFromSubs(Context* context,
const Type* ret = nullptr;

if (auto rec = receiverType->toRecordType()) {
auto instantatiatedFrom = subs.size() == 0 ? nullptr :
root->toRecordType();
auto instantatiatedFrom = subs.empty() ? nullptr : root->toRecordType();
ret = RecordType::get(context, rec->id(), rec->name(),
instantatiatedFrom,
subs);
} else if (auto cls = receiverType->toClassType()) {
auto oldBasic = cls->basicClassType();
CHPL_ASSERT(oldBasic && "Not handled!");

auto instantatiatedFrom = subs.size() == 0 ? nullptr :
root->toBasicClassType();
bool genericParent =
superType && superType->instantiatedFromCompositeType() != nullptr;
auto instantiatedFrom =
(subs.empty() && !genericParent) ? nullptr : root->toBasicClassType();

auto basic = BasicClassType::get(context, oldBasic->id(),
oldBasic->name(),
superType,
instantatiatedFrom,
instantiatedFrom,
subs);
auto manager = AnyOwnedType::get(context);
auto dec = ClassTypeDecorator(ClassTypeDecorator::BORROWED_NONNIL);
Expand All @@ -309,7 +310,8 @@ const Type* InitResolver::computeReceiverTypeConsideringState(void) {
DefaultsPolicy::USE_DEFAULTS);
CompositeType::SubstitutionsMap subs;

bool genericParent = superType_ && superType_->substitutions().size() != 0;
bool genericParent =
superType_ && superType_->instantiatedFromCompositeType() != nullptr;

if (!rfNoDefaults.isGeneric()) {
if (genericParent) {
Expand Down Expand Up @@ -364,7 +366,7 @@ const Type* InitResolver::computeReceiverTypeConsideringState(void) {
}
}

if (subs.size() != 0 || genericParent) {
if (!subs.empty() || genericParent) {
const Type* ret = ctFromSubs(ctx_, initialRecvType_, superType_, ctInitial, subs);
CHPL_ASSERT(ret);
return ret;
Expand Down
45 changes: 45 additions & 0 deletions frontend/test/resolution/testInitSemantics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1348,9 +1348,54 @@ static void testInheritance() {
proc init(type A) {
super.init(A);
}
proc doNothing() {}
}
var x = new Child(int);
// ensure we can call a method on this receiver type after init
x.doNothing();
)""";

auto vars = resolveTypesOfVariables(context, program, {"x"});
auto x = vars["x"];

std::stringstream ss;
x.type()->stringify(ss, chpl::StringifyKind::CHPL_SYNTAX);
assert(ss.str() == "owned Child(int(64))");
}

// Generic grandparent, concrete parent, concrete child
{
Context ctx;
Context* context = &ctx;
ErrorGuard guard(context);

std::string program = R"""(
class Grandparent {
type A;
}
class Parent : Grandparent {
proc init(type A) {
super.init(A);
}
}
class Child : Parent {
proc init(type A) {
super.init(A);
}
proc doNothing() {}
}
var x = new Child(int);
// ensure we can call a method on this receiver type after init
x.doNothing();
)""";

auto vars = resolveTypesOfVariables(context, program, {"x"});
Expand Down

0 comments on commit 9bf3a88

Please sign in to comment.