Editions

New Editions Process

When creating new editions, be sure to:

  • Add the new edition name to the array of editions in the compiler just before pre-edition.

  • Update all desired changes that are currently associated with pre-edition to be linked with this new edition name.

  • When https://github.com/chapel-lang/chapel/issues/27336 gets implemented, be sure to update it as well.

Associating Changes With An Edition

The implementation of editions is currently in progress. Here are some supported ways to tie a change to a particular edition.

The @edition Attribute

For breaking changes that can exist entirely in module code, the @edition attribute can be used.

The @edition attribute requires one or both of the following arguments:

  • first

    • To specify the earliest edition that will include this symbol. When not specified, will be the earliest supported edition.

  • last

    • To specify the final edition that will include this symbol. When not specified, will be pre-edition.

When using the attribute, the old behavior should be marked with @edition(last="<most recent edition name>"), while the new behavior should be marked with @edition(first="pre-edition"). This will ensure that the old behavior remains available with previous editions, while the new behavior becomes available in the pre-edition. E.g.,

@edition(first="pre-edition")
proc foo(x: int) {
  writeln("in new edition foo with arg x=", x);
}

@edition(last="2.0")
proc foo(x: int) {
  writeln("in old edition foo with arg x=", x);
}

Note

Though default is a supported value for the --edition compilation flag, it is not supported for either the first or last argument of the edition attribute. These arguments must be concrete, specific editions, to avoid potential implementation bugs.

Generally, specifying both first and last is most helpful if a feature has been changed in multiple editions. It is hoped that such occurrences will be rare.

Compiler Changes

When making a change that should be supported by the compiler itself, one should be careful to indicate the range of editions to which the change is applicable, rather than specifying a single edition. There is a helper function to use to validate that the compiled edition is within a known first and last edition, which can be used to determine if the change should apply. There is also a helper function to validate that the edition range specified itself is valid.

The Symbol AST node in the production compiler will return the default values listed in The @edition Attribute if they are not explicitly provided. This will be useful when extending attribute support to more than just function resolution, but for specific compiler-level breaking changes the range will likely have to be explicitly specified.

All known editions are stored in an array in the compiler in order, and the last edition in the array is always pre-edition.

Runtime Changes

Since users will specify the edition to use via a compilation flag, it’s likely better to avoid edition-specific computations in the runtime. That doesn’t preclude the possibility of needing certain implementation decisions to happen at runtime, but in general it’s probably better to have done the computation of which implementation to use based on the compiled edition during compilation.

As a concrete example, changing the printing behavior of nan in complex numbers requires modifying our runtime I/O code (https://github.com/chapel-lang/chapel/pull/27011). The best way to handle this is probably adding an argument to qio_channel_print_complex to indicate which version of the implementation to use, and then using the attribute solution in module code to switch between how that argument is set. Something like:

@edition(first="pre-edition")
proc chpl_print_complex(...) {
  return qio_channel_print_complex(false, _channel_internal, re, im,
                                   numBytes(re.type),
                                   /* edition applicable */ true);
}

@edition(last="2.0")
proc chpl_print_complex(...) {
  return qio_channel_print_complex(false, _channel_internal, re, im,
                                   numBytes(re.type),
                                   /* edition applicable */ false);
}

This will make use of the existing edition computations in the compiler (saving execution time performance), and allow the runtime code to use a descriptive name for the argument that reflects the behavior being changed. Alternatively, a separate overload could be added to the runtime to handle the change.