New Issue: dyno: representing 'param' tuples in vararg functions and beyond

20487, "DanilaFe", "dyno: representing 'param' tuples in vararg functions and beyond", "2022-08-22T19:29:37Z"

Variable argument functions in Chapel are backed by tuples. For a function like the following:

proc varArgFn( args...) {
  var varArgRet : args.type;
  return varArgRet;
}

When called with arguments 1 and "hello", is effectively equivalent to:

proc varArgFn(const ref args: _tuple(const in int(64), const ref string)) { ... }

The same is true for param varargs:

proc varArgFn( param args...) {
  var varArgRet : args.type;
  return varArgRet;
}
// Equivalent to
proc varArgFn (param args: _tuple(param int(64) = 1, param string = "hello") { ... }

And herein lies the issue. Dyno and Chapel do not (currently) support param tuples, so even the above representation of the instantiated function is a little bit of a misnomer / abuse of language. Not only that, but it leads to some confusion with genericity / isUnknown, since one of the rules for a Dyno QualifiedType being generic is:

A qualified type is generic if it's a param but doesn't have a param value.

E.g., param int is generic, param int = 1 is concrete. A param _tuple as shown above does not have a value (no = ...!) so at this immediate moment in the Dyno implementation, it's considered generic. However, semantically, the value of the tuple is known and encoded in the tuple type's arguments.

This issue is about such param tuples that arise in the implementation of varargs. How should we represent them? Another thing to keep in mind is that we are thinking about adding param tuple support to Dyno anyway, so could the param tuples in varargs be unified with the "regular" param tuples in this hypothetical implementation? What representation of param tuples should be used in general, with the vararg case in mind?

There are some immediate alternatives that come to mind:

  1. Remain unchanged, as param _tuple(param int(64) = 1 ...). In this case, we probably want to add special case logic for determining whether or not a tuple is generic (since the tuple listed just before is not generic).
  2. Add a TupleParam, so the tuple is represented as param _tuple(int(64),...) = (1, ...).
  3. Both (1) and (2), param _tuple(param int(64) = 1 ...) = (1, ...). There is some redundancy involved, but @mppf points out that different parts of Dyno might find it more convenient to pull data from different places.

Please share your thoughts in the comments!