pic_error Module

Unified error handling with stack traces for PIC. Use error_t as an intent(out) or intent(inout) argument to propagate errors with context through call chains.

Basic usage: type(error_t) :: err call err%set(ERROR_IO, “failed to open file”) if (err%has_error()) call err%fatal() ! Or using operator: if (.haserror. err) call err%fatal()

Error wrapping (Rust-style “caused by”, outermost first): call low_level_routine(err) if (err%has_error()) then call err%wrap(ERROR_PARSE, “failed to parse input”) return end if

Purity

Every mutator (set, clear, wrap, add_context) and the error_raise helper are pure, so they can be used from the pure and pure recursive procedures that make up most of PIC’s sorting and array code. The reporting procedures (get_full_trace, print_trace, fatal) perform I/O and are therefore necessarily impure: calling err%fatal() from a pure procedure is a compile error by design. A pure procedure must return the error to its caller and let an impure caller report it.

Thread safety

error_t carries no locking of any kind. PIC has threaded code paths (pic_array exposes set_threading_mode, and sorting can be threaded), so the rule is:

  • give every thread its own error_t (a thread-private local, or one element of a per-thread array), then reduce/inspect them after the parallel region, or
  • write to a single shared error_t only from inside a critical region (or an equivalent lock).

Two threads calling set, wrap, add_context or clear on the same error_t is a data race: message is an allocatable component, so concurrent writes can corrupt the heap, not merely lose a message. Reading a shared error_t (has_error, is, get_code) while another thread writes it is equally a race.

Error code ranges

PIC reserves codes 0 .. PIC_ERROR_CODE_MAX (0-99) for itself and will only ever add new library codes inside that range. Downstream code that wants its own codes must take them at or above PIC_ERROR_CODE_MAX + 1 (i.e. 100 and up); is_pic_error_code checks a candidate value. Because code_to_string cannot know a downstream name, pass it:

integer(default_int), parameter :: MYLIB_ERROR_SOCKET = 100 print *, code_to_string(err%get_code(), “MYLIB_ERROR_SOCKET”)

The user_name argument is only consulted for codes PIC does not know, so it is safe to pass unconditionally. Error codes Stack trace configuration


Uses

  • module~~pic_error~~UsesGraph module~pic_error pic_error module~pic_global_definitions pic_global_definitions module~pic_error->module~pic_global_definitions module~pic_types pic_types module~pic_error->module~pic_types module~pic_global_definitions->module~pic_types iso_c_binding iso_c_binding module~pic_types->iso_c_binding iso_fortran_env iso_fortran_env module~pic_types->iso_fortran_env

Used by

  • module~~pic_error~~UsedByGraph module~pic_error pic_error module~pic_array pic_array module~pic_array->module~pic_error module~pic_cli pic_cli module~pic_cli->module~pic_error module~pic_tokenizer pic_tokenizer module~pic_cli->module~pic_tokenizer module~pic_clock pic_clock module~pic_clock->module~pic_error module~pic_command_line pic_command_line module~pic_command_line->module~pic_error module~pic_csr pic_csr module~pic_csr->module~pic_error module~pic_fixed_array pic_fixed_array module~pic_fixed_array->module~pic_error module~pic_graph pic_graph module~pic_graph->module~pic_error module~pic_graph->module~pic_csr module~pic_heap pic_heap module~pic_graph->module~pic_heap module~pic_hash_map pic_hash_map module~pic_hash_map->module~pic_error module~pic_heap->module~pic_error module~pic_random_dist pic_random_dist module~pic_random_dist->module~pic_error module~pic_rng pic_rng module~pic_random_dist->module~pic_rng module~pic_rng->module~pic_error module~pic_serialize pic_serialize module~pic_serialize->module~pic_error module~pic_soa pic_soa module~pic_soa->module~pic_error module~pic_soa->module~pic_serialize module~pic_soa_particle pic_soa_particle module~pic_soa_particle->module~pic_error module~pic_soa_particle->module~pic_soa module~pic_sorting pic_sorting module~pic_sorting->module~pic_error module~pic_sorting_ord_sort pic_sorting_ord_sort module~pic_sorting_ord_sort->module~pic_error module~pic_sorting_ord_sort->module~pic_sorting module~pic_sorting_radix_sort pic_sorting_radix_sort module~pic_sorting_radix_sort->module~pic_error module~pic_sorting_radix_sort->module~pic_sorting module~pic_sorting_sort_index pic_sorting_sort_index module~pic_sorting_sort_index->module~pic_error module~pic_sorting_sort_index->module~pic_sorting module~pic_tokenizer->module~pic_error module~pic_vector pic_vector module~pic_vector->module~pic_error module~pic_profiler pic_profiler module~pic_profiler->module~pic_sorting module~pic_random_dist_real pic_random_dist_real module~pic_random_dist_real->module~pic_rng module~pic_sorting_sort pic_sorting_sort module~pic_sorting_sort->module~pic_sorting

Variables

Type Visibility Attributes Name Initial
integer(kind=default_int), public, parameter :: ERROR_ALLOC = 5
integer(kind=default_int), public, parameter :: ERROR_BOUNDS = 7

an index or extent was out of range

integer(kind=default_int), public, parameter :: ERROR_GENERIC = 1
integer(kind=default_int), public, parameter :: ERROR_INTERNAL = 6

a PIC invariant was violated: this is a bug in the library itself, not bad input from the caller. Report it upstream.

integer(kind=default_int), public, parameter :: ERROR_IO = 2
integer(kind=default_int), public, parameter :: ERROR_PARSE = 3
integer(kind=default_int), public, parameter :: ERROR_VALIDATION = 4
integer(kind=default_int), public, parameter :: PIC_ERROR_CODE_MAX = 99

Highest error code PIC reserves for itself. Downstream libraries must number their own codes from PIC_ERROR_CODE_MAX + 1 upwards.

integer(kind=default_int), public, parameter :: SUCCESS = 0
integer(kind=default_int), private, parameter :: MAX_CAUSE_DEPTH = 8
integer(kind=default_int), private, parameter :: MAX_CAUSE_MSG_LEN = 256
integer(kind=default_int), private, parameter :: MAX_LOCATION_LEN = 128

Cause chain configuration

integer(kind=default_int), private, parameter :: MAX_STACK_DEPTH = 20
character(len=*), private, parameter :: TRUNCATION_MARKER = "..."

Marker used whenever a location or cause message does not fit in its fixed-length slot. Locations keep their tail (the :line part) and get the marker as a prefix; messages keep their head and get it as a suffix.

Unified error type with stack trace support


Interfaces

public interface operator(.haserror.)

Operator for checking error state: if (.haserror. err) then

  • private pure function error_has_error(self) result(has_err)

    Check if an error is set

    Arguments

    Type IntentOptional Attributes Name
    class(error_t), intent(in) :: self

    Return Value logical


Derived Types

type, public ::  error_t

Components

Type Visibility Attributes Name Initial
character(len=MAX_LOCATION_LEN), public :: call_stack(MAX_STACK_DEPTH)

Call locations

Read more…
integer(kind=default_int), public :: cause_codes(MAX_CAUSE_DEPTH)

Error codes of wrapped causes

integer(kind=default_int), public :: cause_depth = 0

Number of wrapped causes

character(len=MAX_CAUSE_MSG_LEN), public :: cause_messages(MAX_CAUSE_DEPTH)

Messages of wrapped causes

integer(kind=default_int), public :: code = SUCCESS

Error code (0 = no error)

character(len=:), public, allocatable :: message

Error message

Read more…
integer(kind=default_int), public :: omitted_causes = 0

Number of intermediate causes dropped because the cause chain was already MAX_CAUSE_DEPTH deep. The root cause is always kept; it is the middle layers that are lost, and the traces report how many.

integer(kind=default_int), public :: stack_depth = 0

Current stack depth

Type-Bound Procedures

procedure, public :: add_context => error_add_context
procedure, public :: clear => error_clear
procedure, public :: fatal => error_fatal
procedure, public :: get_code => error_get_code
procedure, public :: get_full_trace => error_get_full_trace
procedure, public :: get_message => error_get_message
procedure, public :: has_error => error_has_error
procedure, public :: is => error_is
procedure, public :: print_trace => error_print_trace
procedure, public :: set => error_set
procedure, public :: wrap => error_wrap

Functions

public pure function code_to_string(code, user_name) result(name)

Map an error code to a human-readable name

Read more…

Arguments

Type IntentOptional Attributes Name
integer(kind=default_int), intent(in) :: code
character(len=*), intent(in), optional :: user_name

Return Value character(len=:), allocatable

public pure function is_pic_error_code(code) result(reserved)

True when code falls inside the range PIC reserves for its own error codes (0 .. PIC_ERROR_CODE_MAX). Downstream libraries can assert their own codes are outside it: if (is_pic_error_code(MYLIB_ERROR_SOCKET)) error stop “code clash”

Arguments

Type IntentOptional Attributes Name
integer(kind=default_int), intent(in) :: code

Return Value logical

private pure function clip_head(location) result(clipped)

Fit a location into MAX_LOCATION_LEN keeping its TAIL, marking any truncation with a leading TRUNCATION_MARKER.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: location

Return Value character(len=MAX_LOCATION_LEN)

private pure function clip_tail(message) result(clipped)

Fit a cause message into MAX_CAUSE_MSG_LEN keeping its HEAD, marking any truncation with a trailing TRUNCATION_MARKER.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: message

Return Value character(len=MAX_CAUSE_MSG_LEN)

private pure function error_get_code(self) result(code)

Get the error code

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self

Return Value integer(kind=default_int)

private function error_get_full_trace(self) result(trace)

Get complete error message with cause chain and stack trace Returns a dynamically-sized multi-line string

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self

Return Value character(len=:), allocatable

private pure function error_get_message(self) result(message)

Get the error message (without stack trace)

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self

Return Value character(len=:), allocatable

private pure function error_has_error(self) result(has_err)

Check if an error is set

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self

Return Value logical

private pure function error_is(self, code) result(matches)

Check if the error matches a specific error code

Read more…

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self
integer(kind=default_int), intent(in) :: code

Return Value logical

private pure function omitted_note(count) result(note)

One-line notice that count intermediate causes were dropped

Arguments

Type IntentOptional Attributes Name
integer(kind=default_int), intent(in) :: count

Return Value character(len=:), allocatable


Subroutines

public pure subroutine error_raise(err, code, message)

Set an optional error argument, if the caller supplied one.

Read more…

Arguments

Type IntentOptional Attributes Name
type(error_t), intent(inout), optional :: err
integer(kind=default_int), intent(in) :: code
character(len=*), intent(in) :: message

private pure subroutine error_add_context(self, location)

Add a call location to the stack trace Typically called when propagating errors upward

Read more…

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(inout) :: self
character(len=*), intent(in) :: location

private pure subroutine error_clear(self)

Clear the error state, stack trace, and cause chain

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(inout) :: self

private subroutine error_fatal(self, unit, exit_code)

Print the error trace and stop the program Use for unrecoverable errors

Read more…

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self
integer(kind=default_int), intent(in), optional :: unit
integer(kind=default_int), intent(in), optional :: exit_code

private subroutine error_print_trace(self, unit)

Print error with cause chain and stack trace to specified unit If unit not specified, prints to stdout

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(in) :: self
integer(kind=default_int), intent(in), optional :: unit

private pure subroutine error_set(self, code, message)

Set an error with code and message Resets the stack trace and cause chain

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(inout) :: self
integer(kind=default_int), intent(in) :: code
character(len=*), intent(in) :: message

private pure subroutine error_wrap(self, code, message)

Wrap the current error with a higher-level context Pushes the current error into the cause chain and sets a new top-level code and message (Rust-style “caused by”)

Read more…

Arguments

Type IntentOptional Attributes Name
class(error_t), intent(inout) :: self
integer(kind=default_int), intent(in) :: code
character(len=*), intent(in) :: message