Error Handling Modes and Prototype Modules

Overview

The Chapel language supports throw, try, try!, catch, and throws which are described below. Chapel supports several error handling modes, and in particular the default for code not in an explicit module declaration is suitable only for prototype code.

For a code-oriented introduction to error handling, refer to the Error Handling primer.

Please see the Error Handling chapter of the language specification for details.

Error Handling Modes

Chapel currently supports three error handling modes: fatal, relaxed, and strict. Developers can select each mode in the following ways:

  • fatal mode

    • writing code in an implicit module

    • writing code in a prototype module

    • passing the --permit-unhandled-module-errors flag on the chpl command line

  • relaxed mode

    • writing code inside of a module declaration

  • strict mode

    • writing code inside of a module marked with a pragma

It is fully permissible to write for a stricter error handling mode while using a looser one – in particular, code that compiles in strict mode will also compile in relaxed or fatal mode.

Fatal Mode for Implicit and Prototype Modules

In implicit and prototype modules, it is not necessary to explicitly handle errors from a function that throws. If an error is thrown and the calling function throws, the error will be propagated out of the function. However, if an error is thrown and the calling function does not throw, the program will halt.

This is the fatal error handling mode.

An implicit module is the module the compiler creates to store code not in a module declaration. In particular, all of the statements above this point in this file are in an implicit module called errorHandling. Since the below statements are also not in a module declaration, they are also in an implicit module.

canThrow(1); // handling can be omitted; halts if an error occurs

proc throwsErrorsOn() throws {
  // error propagates out of this function
  canThrow(-1);
}

proc doesNotThrowErrorsOn() {
  // causes a halt if called
  alwaysThrows();
}

Fatal error mode can also be activated for explicit modules using the prototype module declaration:

prototype module PrototypeModule {

  canThrow(1); // handling can be omitted; halts if an error occurs

  proc throwsErrorsOn() throws {
    // error propagates out of this function
    alwaysThrows();
  }

  proc doesNotThrowErrorsOn() {
    // causes a halt if called
    alwaysThrows();
  }
}

use PrototypeModule;

Relaxed Mode for Explicit Modules

In non-prototype explicit modules, it is necessary to handle errors if the calling function does not throw. If the calling function does throw, then the error will be propagated out as with implicit modules.

This is the relaxed error handling mode.

module ProductionModule {
  // this line would cause a compilation error since the error is not handled
  // canThrow(1);

  proc throwsErrorsOn() throws {
    // any error thrown by alwaysThrows will propagate out
    alwaysThrows();
  }

  // this function does not compile because the error is not handled
  // proc doesNotThrowErrorsOn() {
  //   alwaysThrows();
  // }
}

use ProductionModule;

Strict Mode

It is possible to request a more restricted error handling mode within a module scope using a pragma. With it, it is necessary to explicitly mark all throwing calls, whether or not the calling function throws. The intent of this restricted mode is to make the control flow clear.

This is the strict error handling mode.

The difference between relaxed and strict mode is that throwing calls in throwing functions need to be marked. This may be accomplished with the catch-less try block, the single statement try, or the assignment try.

It is also possible to use try! in these forms.

pragma "error mode strict"
module StrictModule {
  proc throwsErrorsOn() throws {
    try {
      alwaysThrows();
      writeln("never reached");
    }
  }

  proc alsoThrowsErrorsOn() throws {
    try alwaysThrows();
  }

  proc doesNotThrowErrorsOn() {
    try! alwaysThrows();
  }

  proc assignmentTry() throws {
    var x = try alwaysThrows();
    writeln(x);
  }
}

Current Limitations

  • Error handling does not work yet with initializers.

  • It is not yet decided whether or not it will be possible to throw from a deinit function.

  • Errors can’t currently be thrown from iterators that are not inlined by the compiler

    • note that iterators with a single yield as well as leader and standalone iterators are generally inlined by the compiler