Currently our standard function isClass() returns false on an extern class or nil.
Should it return true?
How about introduce these other functions:
isExternClassisNilisPointer - returns true for: ddata, c_ptr, class, extern class, nilShould it be an error to write c1 == c2 where c1 and c2 are of different extern class types? if c1 is a Chapel class and c2 is an extern class?
Note: these are purely user-facing questions.
An implementation for isClass returning true on extern classes is in #6579. This issue (hopefully) contains the brief discussion there.
The current proposal (a mix of Michael and Vass suggestions):
isClass - true if Chapel class or nilisExternClass - true if extern class or nilisPointer - true if: ddata, c_ptr, class, extern class, nil== is an error between two different extern classes== is an error between an extern class and a Chapel classisClass return true on extern classes?delete it.c_ptr() to something.object.isClass return true on nil?We have proc chpl__delete(arg) in our internal modules, which implements Chapel's delete operator. One thing it does is report an error when delete is used illegally. Today it says if (isRecord(arg)) then compilerError("delete not allowed on records"). So if delete is invoked, say, on a domain (as in BenA's future), chpl__delete misses it. The compiler catches it later, at which time caller location is not easily available. So instead I would like chpl__delete to report an error whenever arg is not a class - because this will catch all cases, not just records. Given that we call delete on extern classes as well, we need to check on those too. This led me to the question of isClass on extern classes.
However, this issue is purely about user-facing interface. Error reporting can be fixed simply by adding isExternClassType. Which was the first version of #6579. While writing the PR message, I realized I could just fix isClass so that I don't need to justify having a separate isExternClass. Apparently Michael disagrees that that would be a "fix".
@vasslitvinov : Could you capture the motivating case that led to these questions on the issue or in the comments for reference? (I know you've told me before, but I forget, and others probably will be curious as well).
Here are a bunch of reactions:
Should
isClass()return true for anextern?
I don't feel very strongly about this one either way as long as it's well-defined. Part of my reaction comes from the fact that I wouldn't be surprised if extern classes disappeared in the medium- to long-term.
Should
isClass()return true fornil?
My intuition is "yes."
isExternClass
I wouldn't object to adding this. If isClass returns false for extern classes. If it returned true, I might wonder if we should just have a set of isExtern[Type | Value | ]() tests.
isNil
Does this add value beyond writing 'x == nil`?
isPointer- returns true for: ddata, c_ptr, class, extern class, nil
I'm not crazy about having an isPointer routine for general use since we don't really want to expose pointers within Chapel. With respect to the specific cases you give:
ddata is an internal type that most users shouldn't be reasoning about and which we don't want to document or restrict our ability to remove/change;c_ptr seems like it could/should get its own routine if it needs one (that would also respond to c_void_ptr?).nil in with these other types, nor refer to them as pointers.extern class -- again, no strong opinion, I don't see this as a long-term feature.Should it be an error to write
c1 == c2where c1 and c2 are of different extern class types? if c1 is a Chapel class and c2 is an extern class?
What do you mean by error? A resolution error? (e.g., "I can't find a routine to do that comparison"). If so, I agree that this seems appropriate for a Chapel class vs. an extern class. For two extern classes, I don't have a strong opinion.
isClass- true if Chapel class or nil
OK.
isExternClass- true if extern class or nil
OK.
isPointer- true if: ddata, c_ptr, class, extern class, nil
I'm not a big fan of this. If we need it for our own purposes, can we just make it an internal / non-documented routine? If we don't need it, can we just skip it?
==is an error between two different extern classes
It appears we do permit equality checks between two Chapel classes, so perhaps we should for two extern classes as well for symmetry? (maybe they both "inherit" from "extern object"?)
==is an error between an extern class and a Chapel class
Depends on what "error" means here. If "unresolved routine" then sounds good to me.
@bradcray thank you for the responses. I added my motivating case at the bottom of the issue.
I started out not having a strong opinion about this but I'm more and more convinced that isClass should return false for an extern class because I think any Chapel class should inherit from object.
Part of my reaction comes from the fact that I wouldn't be surprised if extern classes disappeared in the medium- to long-term.
I agree about that part.
Re. == being an error across types - I think we're talking about a resolution error here.
Sounds like the baseline proposal the 3 of us might agree on is this:
isClass - true if Chapel class or nilisExternClass - true if extern class or nil== is an error between two different extern classes== is an error between an extern class and a Chapel class(i.e. removing isPointer from the recent proposal in the PR message).
I'm fine with isClass explicitly not including extern classes, and adding a function isExternClass.
It seems a little funny to me that nil would return true for both of these until it gets cast to a particular type, but since it can be cast to either a Chapel class or an extern class, that seems reasonable. Nil is a funny thing, after all.
I also am in favor of removing isPointer from the proposal.
I think having == be an error when two different extern classes are compared, or when an extern class and a Chapel class are compared is reasonable, though having it be a pointer comparison is also fine.
This is the only part of Michael's revised proposal that gives me pause:
== is an error between two different extern classes
I probably don't care deeply about this case, but I was arguing that since we permit == between two different Chapel classes, perhaps we should do so for extern classes for symmetry. What is the counterargument?
I'm not trying to change how == works but I thought == between two Chapel classes with incompatible types (ie. not super/subclass) would be an error. I think I was wrong about the current behavior there.
I don't have a strong opinion about changing this behavior so would agree with @bradcray that we should do whatever is most consistent.
So, let's do this:
isClass - true if Chapel class or nilisExternClass - true if extern class or nil== is allowed between two different extern classes as it is with Chapel classes== is an error between an extern class and a Chapel classI thought == between two Chapel classes with incompatible types (ie. not super/subclass) would be an error. I think I was wrong about the current behavior there.
I'm guessing that the original rationale might have been that all classes are subclasses of object? But maybe that's a bad rationale. What would Java do?
Java does not allow == between incompatible types (not super/subclass).
K.java:
public class K {
int i;
}
J.java:
public class J /*extends K*/ {
int a;
public static void main(String[] args) {
System.out.println("Hello");
J jj = new J();
K kk = new K();
boolean x = (jj == kk);
if (x) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
$ javac J.java K.java
results in
J.java:10: error: incomparable types: J and K
boolean x = (jj == kk);
^
1 error
but uncommenting the commented-out extends K allows it to compile (because now class J and class K are related, one inherits from the other).
OK, thanks. In that case, I might retract my previous comments and propose reverting to your previous proposal:
- == is an error between two different extern classes
The arguments being:
== between two unrelated classes, and we don't have inheritance information for extern classes (I think..? or do we?)But I could still go either way on this one if others felt more strongly about one option or the other.
@bradcray - that sounds good to me
Thanks everyone for reaching consensus. Who is in favor of having simply
proc isExternClassType(type t) param
rather than all also isExternClass(type or value formal)?
@vasslitvinov - I'm for the simplest possible answer there, for now.
I have an adjustment:
isExternClass - true if extern class; false for nilUser explanation is that since extern classes are disjoint from Chapel classes and nil is a Chapel class w.r.t. isClass, then it should not be also an extern class.
Motivation: avoid ambiguities. For example:
proc =(ref a, b) where isClassType(a.type)
proc =(ref a, b) where isExternClassType(a.type)
Considerations?
In the documentation for extern classes, do we suggest that nil is a valid value to store into them? If not, does the user have some access to c_null? Or what would they use in Chapel to refer to a NULL pointer?
In your motivating example, is it legal to pass nil into a ref argument? I'd guess it shouldn't be...
Belatedly, it also occurs to me: In your motivating example, you're calling the isExternClassType() function which makes this question more about what nil.type means than about what isExternClassType() should do about nil. Specifically, I'd expect only isExternClass() and isExternClassValue() to know anything about nil.
Good considerations.
Currently nil cannot be "assigned" into an extern-class variable. However, extern-class variables are initialized by default to nil. So I consider the former an omission in the implementation. Does it make you feel that nil should be isExternClass, too?
isExternClass and isExternClassType are really synonymous even w.r.t. nil. While this is a technicality that few users will encounter, we can pass nil around in any manner - including as a ref and as a type. For example:
var myNil = nil;
passByRef(myNil);
passByIn(nil);
proc passByIn(in arg) { passByRef(arg); }
proc passByRef(ref arg) { writeln("yes this works"); }
writeln(makeNilType(nil):string);
proc makeNilType(arg) type return arg.type;
isExternClass. If you disagree, I can add that, too.Does it make you feel that nil should be isExternClass, too?
If we were to support that routine, yes, but I thought the current plan was not to do so.
we can pass nil around in any manner - including as a ref and as a type.
This sounds like a bug to me, and therefore not a case we should put effort into worrying about. I'd suggest filing bug futures against the fact that these patterns are supported today as placeholders rather than worrying about cases that get ambiguities due to doing so.
I don't disagree with anything recently mentioned, but if this goes on for any longer, I'm going to switch to advocating that we completely remove extern class from the language.
Clarifying some misunderstandings above -- I thought @vasslitvinov was saying that nil can be passed to a ref x argument or a type x argument, which seemed clearly broken to me (nil doesn't have an l-value and isn't a type). I now believe he was saying that a variable that is storing the value nil can be passed to a ref argument and that its type (or nil.type) can be passed to a type argument which doesn't trouble me.
I think the question that would've short-circuited a lot of the confusion above would've been: "Should isExternClassType() return true or false for nil.type?" My answer is false, as is Vass's I believe.
Correct. Sorry for the confusion.
Implemented / closed by #6674
As per this discussion, we will not merge #6579 (where isClass returns true on extern classes).