-
Notifications
You must be signed in to change notification settings - Fork 419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should the compiler stop supporting type constructors for types whose fields have generic type? #21993
Comments
Note that we could consider such types to generate a warning or requirement for users to supply a type constructor as proposed in #21992. Or we could take the approach suggested here and just say that there is not usable / complete type constructor unless the user does (which is not the worst thing in the world if one leans on type inference heavily and doesn't want to declare the types of things [precisely]). |
I think this approach is interesting, especially if it applies to both fields like Also, I think that we will need to either apply this strategy — or outright ban fields declared like |
I've been looking at not allowing type construction for records like Details of patch:
In particular, this pattern came up in ChapelHashtable: record chpl_TableEntry {
var status: chpl__hash_status = chpl__hash_status.empty;
var key;
var val;
}
record chpl__hashtable {
type keyType;
type valType;
...
var tableSize: int;
var table: _ddata(chpl_TableEntry(keyType, valType)); // 0..<tableSize
...
proc init(type keyType, type valType, ...) { ... }
} Now, if we go with the proposal, we can no longer write I thought about several ways to adapt the code, but I found each of them pretty unsatisfying:
I am thinking about alternative solutions to #16508 and I will post about them there. |
I'm writing this comment before moving on to those solutions, just to think about the problem in this context in isolation. This is what I was imagining we'd do:
But I didn't anticipate this:
Huh, interesting... I'd always assumed that when we saw record R {
var x;
} results in: typedef struct chpl_R__array_DefaultRectangularArr_1_int64_t_one__real64_int64_t_chpl_s {
_array_DefaultRectangularArr_1_int64_t_one__real64_int64_t_chpl x;
} R__array_DefaultRectangularArr_1_int64_t_one__real64_int64_t_chpl; while: record R {
type t;
var x: t;
} results in: typedef struct chpl_R__array_DefaultRectangularArr_1_int64_t_one__real64_int64_t_chpl_s {
chpl___RuntimeTypeInfo3 t;
_array_DefaultRectangularArr_1_int64_t_one__real64_int64_t_chpl x;
} R__array_DefaultRectangularArr_1_int64_t_one__real64_int64_t_chpl; Thinking about ways to proceed with the proposal here, could we consider this to be motivation for being able to write a user-defined type initializer for record R {
var x;
}
proc type R.init(type x) {
x.type = x;
}
// or `proc type R.init(type x.type) { }` using John's idea then would we be back on firm ground? The obvious downside is that, until we supported such type initializers, you'd have to either live with or squash the warning; or adopt the less-space-efficient approach as a means of shoving it aside. One other observation here that seems important: If I'm writing a hash table over 3-element Chapel arrays (say), I probably don't want to make it take arrays of a given/shared domain (what the runtime type suggests to me), but to take any 3-element array—particularly if we're copying the array value into the map. In which case, the dynamic type of the array isn't really what we want to be characterizing this type by anyway, it's the static type. That brings to mind past conversations about distinguishing between static vs. dynamic type queries using [edit: The point being that the type author who prefers documenting their parameterized types directly probably should have a way to opt out of storing these fields as well. Or, put another way, I don't think we should be relying on typeless fields as a way to avoid runtime type storage overheads, or defining the language in a way that requires writing it in that pattern to avoid the overhead]. |
Agreed. However I'm not so sure that we need to resolve this for the fall release. The changes you are describing would be additive / non-breaking. And, hypothetically speaking, we could use some kind of pragma or something to opt back in to the type constructor specifically for
I think that #16508 is better solved by preventing default initialization with the compiler-generated default initializer. (see this comment). Is there some other reason that it would be beneficial to limit type constructors in cases like The other thing I can imagine is that we'd like to limit type constructors in this way so that |
For me, the other main reason is that (over time) it has just seemed a bit odd to support I'm slightly less offended by So I think my concern beyond #16508 in opening this issue was just that the compiler-generated type constructors for such cases have increasingly seemed pretty weird and funky all around, such that maybe we should try to avoid considering anything that relies upon them as being stable until user-defined type constructors come on-line (where I still see value in supporting typeless fields and calls that create new instances of them like |
One issue is that the compiler is currently allowing passing named arguments to type constructors even to non-generic fields. For example: record S {
type t1;
type t2;
var x: (t1, t2);
}
type t = S(x=(int, real));
writeln(t:string); Getting an error in this case would be good. |
This comment was marked as resolved.
This comment was marked as resolved.
@mppf opened https://github.com/Cray/chapel-private/issues/5228 to explore closing the buggy behavior he mentioned above. Back on the OP, after discussions with Michael, we thought that maybe the best way forward on this issue was to continue to support positional argument in type constructors/signatures for fields whose types were not complete (such that one could still write Ultimately, I think having some way of passing types using some form of named argument in cases like this would be attractive, but that could be done post-2.0. One idea I had was to write |
I've created a PR that rules out named argument passing to Please see #23067 if we can merge that PR, I think we can close this issue. |
…param (#23067) For #21993 Resolves Cray/chapel-private#5228 This PR adds a compilation error when a type construction call uses named argument passing to set the type of a field that is not a `type` or `param` field. For example: ``` chapel record R { var x; var y: integral; } type t1 = R(x=real, y=int); // error ``` The reason for this error is twofold: 1. We have observed some nonsensical programs being able to compile, and this avoids bugs in this area. See below for an example. 2. We are uncertain if the type of `x` should be named `x` in the type constructor. Perhaps it should have a different name. Of course, this error can be relaxed in the future as a non-breaking change. Here is an example program that this PR causes to fail with an error but (nonsensically) compiles and runs before this PR: ``` chapel record S { type t1; type t2; var x: (t1, t2); } type t = S(x=(int, real)); writeln(t:string); ``` Reviewed by @vasslitvinov - thanks! - [x] full comm=none testing
The PR mentioned in Michael's most recent comment has been merged. Can we close this issue? |
I think so, thanks! |
[This is spun off from discussion on #16508 ] (Lydia edit: to fix the link display)
Currently, when declaring a type:
the compiler generates a type constructor with the signature:
and we have found this type constructor signature to be problematic/confusing/odd in some cases (TODO: link to previous issues). This issue proposes that the compiler essentially not create a well-formed type constructor for such types (or: create an un-callable type constructor) such that the only legal type constraints for such a type are
R(?)
andR
(see #21988 for related discussion of the meaning of these two expressions).Do this not just for untyped fields like
x
here, but for all fields whose declared types are generic.The goal in doing so is to avoid getting into difficult questions like "What should the compiler-generated type initializer be for types like:
while also serving to improve the user's ability to reason about type signatures that has led to issues like #19120, #21404, and #21989.
Open question: For a type like:
should the only legal type constructor call be
R(?)
or shouldR([t=]int, ?)
also be supported? What about:should
R(int, ?, 3)
be supported?R(int, 3, ?)
?R(int, p=3, ?)
?That is, in these examples, should the compiler-generated type constructor accept arguments for all well-formed generic fields and just skip variable fields with generic types? Or perhaps only support the fields up to the point where the first variable field with generic type is encountered?
The text was updated successfully, but these errors were encountered: