Notes, Warnings and Errors

The compiler provides a framework for handling notes, warnings and errors as they’re encountered and created during the compilation process.

This code can be found in the dfmc-conditions library. Initial skeletal API documentation for this library can be found at The DFMC-CONDITIONS API Reference.

Conceptually, all notes, warnings and errors are subtypes of <condition>, so we will often refer to them collectively as program conditions or instances of <program-condition>.

Status of this Library

This library is interesting (to some) as it is in a half-completed state and many things are not yet fully implemented or used. We will try to note these things below when we discuss them.

Philosophy

The Open Dylan compiler tries hard not to abort compilation when an error is noted. This leads to many things that might be fatal errors elsewhere being represented as serious warnings here. For example, an undefined variable reference does not abort compilation, but if the code containing that reference is executed it causes a crash at run time.

This was part of a very exploratory development model under the Open Dylan IDE, where many errors could be corrected while an application was running.

This workflow isn’t commonly used with Dylan today, so we may revise this philosophy and how it applies to the compiler’s handling of program conditions, or at least make it configurable.

Similarly, a key element to how program conditions are currently used today is that when we store values on them, we store them as the raw object so that they’re readily accessible from within the debugger, rather than only storing the string representations.

Program Condition Hierarchy

The root of the condition hierarchy is <program-condition>. This is an abstract class and one of the subclasses such as <program-note>, <program-error> or <program-restart> should be subclassed instead.

Reporting a Program Condition

The typical way to report that a program condition has arisen is to use note. There are other mechanisms, such as raise, restart, simple-note and simple-raise, but these are not in common usage.

For proper error reporting, you will want to try to report as accurate a source location as you possibly can. This can be tricky at first, so look at other similar warnings if you need the assistance.

The actual code for noting a program condition is pretty straightforward, once you’ve identified the location to emit the program condition, and the type of program condition to emit.

note(<wrong-type-in-assignment>,
     variable-name: the-name,
     type: binding-type,
     rhs: rhs-value,
     source-location: fragment-source-location(fragment));

Source Locations

There are a couple of useful rules to follow for getting source locations for noting a program condition during compilation.

  • If you’re in C-FFI, you’re probably working with fragments, and so fragment-source-location is the right function.

  • If you’re in dfmc-definitions, then you probably also want fragment-source-location.

  • If you’re in conversion, you may be dealing with either fragments or model objects. For fragments, you want fragment-source-location. For model objects, you want model-source-location.

  • If you’re in dfmc-optimization, then you likely want dfm-source-location if you’re working with an object that is part of the control flow or data flow graphs, like any computation or temporary. However, in some cases, you’ll still be working with model objects, so keep an eye out for when you need to use model-source-location.

Defining a new Program Condition

Depending on where you are defining your new program condition within the Program Condition Hierarchy, you will need to use the appropriate program condition definer:

An example definition looks like:

define program-warning <ambiguous-copy-down-method>
  slot condition-method, required-init-keyword: meth:;
  slot condition-other-methods, required-init-keyword: other-methods:;
  format-string "Multiple applicable copy-down methods for %s, picking one at random";
  format-arguments meth;
end;

An interesting thing to note here is that the other-methods are being recorded by this <program-note> even though they are not used within the formatted output. This is because the additional values can be useful when viewing the condition within the debugger or by other programmatic processing such as filtering.

PPML, Pretty Print Markup Language

When conditions are stored, their slots are converted to PPML objects. Many objects within the compiler are already configured to be able to generate PPML via the many specializations of as(class == <ppml>, ...) that can be found within the dfmc-debug-back-end (see print-condition.dylan).

Slots are converted to PPML representations via code that is autogenerated by the various definer macros which create a specialization on convert-condition-slots-to-ppml.

Filtering of Program Conditions

This is functionality that has not been completed and is currently not entirely in use.

To be written.

How Warnings Are Displayed and Recorded

To be written.

Responding to a Program Condition

In Dylan, the condition system allows for responses to conditions and can restart a computation with new information. While parts of dfmc-conditions are designed to permit this, this functionality, has never been completed and is not yet working.

Future Work

Look at cleaning up unused API and things that are no longer necessary.

  • obsolete-condition? is probably obsolete.

  • format-condition and related code including <detail-level> are probably no longer necessary with the code in dfmc-debug-back-end and the specialization on print-object present there.

  • The specialization on print-object can probably go away.

  • simple-note and simple-raise can go away.

  • There is a comment in dfmc/conversion/convert.dylan that the presence of dfm-context-id is a hack until true source locations are available. Should we remove context-id and the supporting code? (On a related note, does that implementation of dfm-context-id even work?

Complete other parts of the implementation:

  • Program condition filtering.

  • Program restarts.

  • Make <program-error> distinct from a serious warning. This would also need a change to dfmc-debug-back-end and a specialization on condition-classification.

  • Use more of the various subclasses of <program-note> like the style, performance and portability notes. This requires getting the filtering to work.

  • The implementation doesn’t use limited collection types where it can.

The DFMC-CONDITIONS API Reference

Definers for new Program Conditions

program-condition-definer Macro
Macro Call:

define [modifier*] program-condition *class-name* (*superclasses*)
  *slot-spec*
  format-string *string*;
  format-arguments *slot*, ...;
  filter *filter*;
end program-note;

Parameters:
  • modifier – One or more class adjectives. bnf

  • class-name – A valid Dylan class name. bnf

  • superclasses – One or more Dylan class names to be used as the superclasses for the newly created program condition.

  • slot-spec – A slot specification.

  • format-string – A format string valid for use with format.

  • format-arguments – One or more parameters which will be passed to format along with the format-string. The parameter values will be drawn from the corresponding slots.

  • filter – A Dylan expression to be used as the value for program-note-filter on the new class. This should either be #f or an instance of <function> which returns a boolean value.

Discussion:

This is not typically used outside of the dfmc-conditions library. It is used for creating a new direct subclass of <program-condition>. Most often, program-note-definer or a similar more specific definer macro would be used instead.

Any additional slot specifications will be modified slightly:

  • The constant adjective will be removed if present.

  • The type constraint for the slot will be a type union with <ppml>.

program-note-definer Macro
Macro Call:

define [modifier*] program-note *class-name*
  *slot-spec*
  format-string *string*;
  format-arguments *slot*, ...;
  filter *filter*;
end program-note;

define [modifier*] program-note *class-name* (*superclasses*)
  *slot-spec*
  format-string *string*;
  format-arguments *slot*, ...;
  filter *filter*;
end program-note;

Discussion:

Create a new <program-note> subclass.

performance-note-definer Macro
Discussion:

Create a new <performance-note> subclass. See program-note-definer for details.

portability-note-definer Macro
Discussion:

Create a new <portability-note> subclass. See program-note-definer for details.

program-error-definer Macro
Discussion:

Create a new <program-error> subclass. See program-note-definer for details.

program-restart-definer Macro
Discussion:

Create a new <program-restart> subclass. See program-note-definer for details.

program-warning-definer Macro
Discussion:

Create a new <program-warning> subclass. See program-note-definer for details.

run-time-error-warning-definer Macro
Discussion:

Create a new <run-time-error-warning> subclass. See program-note-definer for details.

serious-program-warning-definer Macro
Discussion:

Create a new <serious-program-warning> subclass. See program-note-definer for details.

style-warning-definer Macro
Discussion:

Create a new <style-warning-note> subclass. See program-note-definer for details.

program-condition-definer-definer Macro
Discussion:

This is not commonly used outside of dfmc-conditions. It is creating new program-conditioner definer macros.

Program Conditions

<program-condition> Open Abstract Class
Superclasses:

<simple-condition>

Init-Keywords:
  • compilation-stage – Defaults to the value of *current-stage*.

  • program-note-creator – Defaults to the value of *current-dependent*.

  • source-location – Defaults to #f. Every effort should be made to supply a valid value for this keyword.

Discussion:

The root of the hierarchy is <program-condition>. All errors, warnings, etc, about code in a program being compiled should be reported as instances of this class.

This class should only be used for type declarations and as the superclass for mixin properties. For instantiable classes, it’s best to subclass one of <program-error>, <program-note>, or <program-restart> instead.

<program-notes> Type
Supertypes:

<sequence>

<program-note> Open Primary Abstract Class
Superclasses:

<warning>, <program-condition>

Init-Keywords:
  • context-id – An instance of <string>.

  • subnotes – A sequence of subnotes, allowing hierarchical explanations to be constructed. See Subnotes.

Discussion:

When a context-id has been supplied, this is used to give an indication of the logical context of the source that the note is about, typically to give a concise textual hint, allowing for example (where "process-foo" is the context-id:

foo.dylan:180:Warning in process-foo: Bogus call to bar.

<program-error> Open Abstract Class
Superclasses:

<program-note>

Discussion:

A <program-error> is a language error. Examples would be (most) syntax errors, inconsistent direct superclasses, or a reference to an undefined name.

<program-restart> Open Primary Abstract Class
Superclasses:

<program-condition>, <restart>

Init-Keywords:
  • default

Discussion:

A <program-restart> is a <restart> meant to be used as part of the recovery protocol for some <program-condition>.

<program-warning> Open Abstract Class
Superclasses:

<program-note>

Discussion:

A <program-warning> is a note about something that might be a mistake in program, but the compiler is able to compile it without intervention.

<run-time-error-warning> Open Abstract Class
Superclasses:

<program-warning>

Discussion:

Run-time-error warnings are given when the compiler can prove that executing the code will lead definitely lead to a run-time error, whether or not that error is handled. These warnings should be hard for the user to suppress. It should be possible for a user to treat these warnings as errors; that is, stop the compilation process because of one.

<serious-program-warning> Open Abstract Class
Superclasses:

<program-warning>

<style-warning> Open Abstract Class
Superclasses:

<program-warning>

Discussion:

Style warnings are given when the compiler detects code in a style that is legal (strictly speaking), but not desirable. The display of style warnings can be inhibited globally, or on a class-by-class basis.

<performance-note> Open Abstract Class
Superclasses:

<program-note>

Discussion:

Performance notes are given when the compiler is prevented from doing an optimization that should be reasonable or expected in the current context. Typical reasons would be that it has insufficient type, sealing, or program flow information.

<portability-note> Open Abstract Class
Superclasses:

<program-note>

Discussion:

Portability notes are given when the compiler detects something that is valid in the Open Dylan compiler, but is not part of portable Dylan or could have undefined effects in Dylan.

It should be possible to turn these warnings into errors, to support a standards-conforming version of the compiler.

Program Condition Slots

condition-compilation-stage Generic function
Signature:

condition-compilation-stage (object) => (value)

Parameters:
Values:
condition-context-id Generic function
Signature:

condition-context-id (object) => (value)

Parameters:
Values:
condition-program-note-creator Generic function
Signature:

condition-program-note-creator (object) => (value)

Parameters:
Values:
condition-source-location Generic function
Signature:

condition-source-location (object) => (value)

Parameters:
Values:

Signaling Program Conditions

note Open Generic function
Signature:

note (class #key #all-keys) => ()

Parameters:
  • class – An instance of subclass(<program-condition>).

Discussion:

The primary program condition signaling interface is note, which calls make on the condition class and signals it, possibly returning. It can be used for any program condition, but is mainly oriented towards <program-note>.

Example:
note(<inaccessible-open-definition>,
     binding: form-variable-binding(form),
     source-location: form-source-location(form));
note(subclass(<program-condition>)) Method
maybe-note Macro
raise Open Generic function
Signature:

raise (class #key #all-keys) => ()

Parameters:
  • class – An instance of subclass(<program-condition>).

Discussion:

This function is analogous to the standard Dylan error function and is guaranteed to not return.

raise(subclass(<program-error>)) Method
restart Open Generic function
Signature:

restart (class #key #all-keys) => ()

Parameters:
  • class – An instance of subclass(<program-restart>).

restart(subclass(<program-restart>)) Method

Preserving Program Conditions

Program conditions are tracked in each library. They are stored in a table that is associated with each <library-description> via library-conditions-table. There are implementations of another generic function, remove-dependent-program-conditions which is commonly invoked during retraction. (What retraction is for isn’t clear to me at this point.)

add-program-condition Generic function
Signature:

add-program-condition (condition) => ()

Parameters:
Discussion:

Records a program condition. This does not usually need to be invoked directly outside of dfmc-conditions where it is usually invoked during the filtering of a program condition.

add-program-condition(<condition>) Method
Discussion:

Runtime errors that are not <program-condition> are not currently tracked. This method doesn’t record them.

add-program-condition(<program-condition>) Method
Discussion:

Preserves a program condition by storing it in the library-conditions-table for the current library being compiled.

library-conditions-table Generic function
Signature:

library-conditions-table (library) => (table)

Parameters:
Values:
  • table – An instance of <table>.

remove-program-conditions-from! Generic function
Signature:

remove-program-conditions-from! (table key stages) => ()

Parameters:

Recovery and Restarting

condition-block Macro
*error-recovery-model* Variable

Subnotes

This is a very rarely used capability within the program condition system and isn’t currently well supported by the compiler output to standard out and standard error.

Any <program-note> can have additional notes attached to it. These notes are useful for attaching extra data to a note, like possible options or the sets of conflicting items.

An example usage of subnotes is:

note(<ambiguous-copy-down-method>,
     meth: m,
     other-methods: others,
     source-location: m.model-source-location,
     subnotes: map(method (m)
                     make(<ambiguous-copy-down-method-option>,
                          meth: m,
                          source-location: m.model-source-location)
                   end,
                   others));

Note

Subnotes are not displayed by the default printing of program conditions by the command line compiler. They can be found in the condition log file that is created during the build process. (_build/build/foo/foo.log)

subnotes Generic function
Signature:

subnotes (object) => (value)

Parameters:
Values:
note-during Macro
accumulate-subnotes-during Macro
*subnotes-queue* Thread Variable

Printing Program Conditions

*detail-level* Thread Variable
Type:

<detail-level>

Discussion:

Note

This is currently ignored.

<detail-level> Type
Equivalent:

one-of(#"terse", #"normal", #"verbose")

Discussion:

A simple, three-tiered approach to the amount of detail a condition presents.

Note

This is currently ignored.

Operations:

format-condition

format-condition Generic function
Signature:

format-condition (stream condition detail-level) => ()

Parameters:
Discussion:

This calls format to write to the stream. The format string and arguments come from the condition’s condition-format-string and condition-format-arguments respectively.

print-object(<program-condition>, <stream>) Method
Signature:

print-object (condition, stream) => ()

Parameters:
Discussion:

This calls format-condition with a detail-level of #"terse".

This is provided for integrating program condition printing with the usual mechanisms for formatted output.

Note

This is not actually called often at all as there is a more specific specialization on <program-note> defined in dfmc-debug-back-end.

Unclassified API

$record-program-note Constant
$signal-program-error Function
Signature:

$signal-program-error (c) => ()

Parameters:
$signal-program-note Function
Signature:

$signal-program-note (c) => ()

Parameters:
<ignore-serious-note> Class
Superclasses:

<program-restart>

Init-Keywords:
  • format-string

  • note

<program-note-filter> Constant
convert-condition-slots-to-ppml Generic function
Signature:

convert-condition-slots-to-ppml (condition) => ()

Parameters:
Discussion:

Converts all slots on a condition to their PPML representation. This is typically autogenerated by the various program condition definer macros. It is called from add-program-condition.

convert-condition-slots-to-ppml(<condition>) Method
convert-condition-slots-to-ppml(type-union(<simple-condition>, <simple-error>, <simple-warning>)) Method
convert-condition-slots-to-ppml(<program-note>) Method
convert-condition-slots-to-ppml(<program-restart>) Method
convert-condition-slots-to-ppml(<program-warning>) Method
convert-condition-slots-to-ppml(<serious-program-warning>) Method
convert-condition-slots-to-ppml(<program-error>) Method
convert-condition-slots-to-ppml(<run-time-error-warning>) Method
convert-condition-slots-to-ppml(<style-warning>) Method
convert-condition-slots-to-ppml(<performance-note>) Method
convert-condition-slots-to-ppml(<portability-note>) Method
convert-condition-slots-to-ppml(<ignore-serious-note>) Method
convert-slots-to-ppml Macro
dfmc-continue Thread Variable
dfmc-restart Thread Variable
do-with-program-conditions Function
Signature:

do-with-program-conditions (body) => (#rest results)

Parameters:
Values:
  • #rest results – An instance of <object>.

interesting-note? Generic function
Signature:

interesting-note? (note) => (interesting?)

Parameters:
Values:
Discussion:

True if the note is interesting to the user, according to the yet-to-be-defined compiler policy object. Uninteresting conditions are suppressed, either by not printing messages for them or not logging them at all. Because all errors and restarts are serious, they are also interesting.

interesting-note?(<program-note>) Method
Parameters:
Values:
  • interesting? – Always returns #t.

interesting-note?(<performance-note>) Method
Parameters:
Values:
  • interesting? – Always returns #f.

make-program-note-filter Generic function
Signature:

make-program-note-filter (#key file-name from to in class action) => (filter)

Parameters:
  • file-name (#key) – An instance of <string>.

  • from (#key) – An instance of <integer>.

  • to (#key) – An instance of <integer>.

  • in (#key) – An instance of <string>.

  • class (#key) – An instance of subclass(<condition>).

  • action (#key) – An instance of <function>.

Values:
obsolete-condition? Open Generic function
Signature:

obsolete-condition? (condition) => (obsolete?)

Parameters:
Values:
obsolete-condition?(<program-condition>) Method
Parameters:
Values:
  • obsolete? – Always returns #f.

Discussion:

Note

This is never used.

present-program-error Generic function
Signature:

present-program-error (condition) => ()

Parameters:
present-program-error(<condition>) Method
present-program-error(<program-note>) Method
present-program-note Generic function
Signature:

present-program-note (condition) => ()

Parameters:
present-program-note(<condition>) Method
present-program-note(<program-note>) Method
program-note-class-= Function
Signature:

program-note-class-= (class) => (pred)

Parameters:
  • class – An instance of subclass(<condition>).

Values:
program-note-file-name-= Function
Signature:

program-note-file-name-= (file-name) => (pred)

Parameters:
  • file-name – An instance of <string>.

Values:
program-note-filter Open Generic function
Signature:

program-note-filter (class) => (filter)

Parameters:
  • class – An instance of subclass(<condition>).

Values:
program-note-filter(subclass(<program-note>)) Method
program-note-filter(subclass(<condition>)) Method
program-note-filter(subclass(<program-warning>)) Method
program-note-filter(subclass(<serious-program-warning>)) Method
program-note-filter(subclass(<run-time-error-warning>)) Method
program-note-filter(subclass(<style-warning>)) Method
program-note-filter(subclass(<performance-note>)) Method
program-note-filter(subclass(<portability-note>)) Method
program-note-filter-setter Open Generic function
Signature:

program-note-filter-setter (filter class) => (filter)

Parameters:
  • filter – An instance of <program-note-filter>.

  • class – An instance of subclass(<program-condition>).

Values:
program-note-filter-setter(<program-note-filter>, subclass(<program-condition>)) Method
program-note-in Function
Signature:

program-note-in (form) => (pred)

Parameters:
Values:
program-note-location-between Function
Signature:

program-note-location-between (from to) => (pred)

Parameters:
Values:
report-condition Open Generic function
Signature:

report-condition (condition) => ()

Parameters:
serious-note? Generic function
Signature:

serious-note? (note) => (serious?)

Parameters:
Values:
Discussion:

True if this note is serious – that is, requires terminating the current processing and picking a restart. The default behavior is that notes are not serious, but the policy object should allow upgrading them, with options like “all warnings are errors” for making <program-warning> serious, or “strict Dylan” for making <portability-note> serious.

Errors are always serious, by definition, because the compiler can’t just skip them. Restarts are always serious, as much as such a definition make sense for them.

serious-note?(<program-note>) Method
Parameters:
Values:
  • serious? – Always returns #f.

serious-note?(<program-error>) Method
Parameters:
Values:
  • serious? – Always returns #t.

serious-note?(<serious-program-warning>) Method
Parameters:
Values:
  • serious? – Always returns #t.

simple-note Generic function
Signature:

simple-note (class format-string #rest args) => ()

Parameters:
  • class – An instance of subclass(<program-note>).

  • format-string – An instance of <string>.

  • args (#rest) – An instance of <object>.

simple-raise Generic function
Signature:

simple-raise (class format-string #rest args) => ()

Parameters:
  • class – An instance of subclass(<program-error>).

  • format-string – An instance of <string>.

  • args (#rest) – An instance of <object>.

with-program-conditions Macro
with-simple-abort-retry-restart Macro