New Issue: Compiler-generated initializers for 'sync t' fields should accept 't'

17326, "bradcray", "Compiler-generated initializers for 'sync t' fields should accept 't'", "2021-03-03T17:11:12Z"

As part of the effort to deprecate implicit reads/writes of sync variables, initialization of sync variables using other sync also becomes a bit more explicit. That is, where before one could write:

var s: sync int;
var t = s;

Now, the second line requires being written as var t = s.readFE(); or some other variant, to make the user's intent w.r.t. the full/empty state explicit. Meanwhile, initializing a variable of type sync t with an expression of type t works and is often the more useful case:

var s: sync int = 42;

These changes make compiler-generated initializers for records or classes with sync fields a bit problematic, since the typical pattern for such a field is:

proc myObj.init(..., s: s.type = s.type.defaultValue, ...) {
  this.s = s;

That is, for a sync field, by default we would accept a sync field argument and use it to initialize the sync field. However, since that relies of default initialization of a sync using another sync, it's not something the user could write, and it's not obvious that the compiler should be making assumptions about what should happen to the full/empty state of the argument (or maybe even the field).

Given that the lack of an init= from sync t to sync t but the presence of an init= from t to sync t, it would make sense to have the pattern for a sync field look more like this:

proc myObj.init(..., s: t = t.defaultValue(), ...) {
  if s.defaultNotUsed then
    this.s = s;

as this would permit the user to pass an integer like 42 in to initialize a sync field. However, it has some challenges as well—notably, that we don't currently (yet) have a way to determine whether a callsite overrode the default value for an argument or not (though that's something that's been desired and discussed for years, with choice of syntax and prioritization being the main barriers to doing it).

More generally, this issue might be considered to ask whether, for any given type t, if that type supports only a single copy initializer and it is not from t to t, should the compiler-generated initializer accept values of the copy initializer's incoming type? Or should syncs be considered a special case in this respect, and any other such cases would require users to write their own initializers for the class in question? (Or, should users be forced to write their own initializers for classes/records containing sync fields as well; that is, the compiler cannot / will not provide one).