Void Variables and Fields

This README describes support for void variables and class/record fields. A variable or field with type void will be removed from the program by the compiler. It cannot be assigned to a variable of any non-void type, and non-void values cannot be assigned to a void variable. A void variable cannot be used in any context that requires a non-void value.

The value with type void is currently named _void. It is expected that this name will be changed.

A void value can be used to instantiate fully-generic function arguments as long as the argument is not used in any non-void context. This will cause the function argument to be removed by the compiler.

How is this useful?

Often a class or record will have different implementation strategies depending on things like the properties of the hardware or the type of a generic field. When the different implementation strategies each require their own class/record fields, the number of fields in the type grows, ballooning the size of the type with unused variables.

The type void was introduced to allow the unused variables to be removed by the compiler. For example, if some fields are used only when the param isAccelerator is true, they can be declared with a conditional type that is void when it is false. Such fields will be removed by the compiler.

record R {
  param isAccelerator: bool;
  var accelVar: if isAccelerator then real else void;

  proc doComputation() {
    if isAccelerator {
      ... accelVar ...
    } else {
      // non-accelerator version
    }
  }
}

Void Functions and Iterators

A function may return the void value _void. Functions that return the value _void have a return type of void and can be used as void values.

A function that does not return also has a return type of void, but can not be used as a void value. For example, it cannot be assigned to a void-variable.

An iterator may yield the void value _void. A loop calling the iterator will iterate once per _void yielded with a void index variable.

An iterator with no yield has type void. A loop calling the iterator will execute the body of the iterator, but no loop iterations.

proc noReturn(): void {
  writeln("Hello from noReturn");
}

//var v1: void = noReturn();
//var v2 = noReturn();

proc explicitReturn(): void {
  writeln("Hello from explicitReturn");
  return _void;
}

var v3: void = explicitReturn();
var v4 = explicitReturn();

iter noYield() {
  writeln("noYield iter");
}

iter yieldVoids() {
  for i in 1..2 do
    yield _void;
}

for i in noYield() {
  writeln("Unprinted");
}

for i in yieldVoids() {
  writeln("Printed twice");
}

Although the function noReturn() has a return type of void, it cannot be assigned to a variable. Both the v1 and v2 assignments above would be errors if uncommented. A function that returns a void value explicitly is assignable to a variable of type void, as in the v3 and v4 examples.

The for loop calling the noYield() iterator will execute the iterator body once, printing "noYield iter", but will not execute the loop body. The loop calling the yieldVoids() iterator will execute the loop body twice because the iterator yields two _void values.