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:

<format-string-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#