Expander warnings and overloaded functions

Posted on May 30, 2016

More work in the ‘typed-languagebranch of Vicare, for both the expander and the built–in types infrastructure. Some more type propagation stuff is implemented for built–in primitives.

Everything I discuss here is relative to code in the head of the ‘typed-language’ branch.

Expander warnings

Since some commits ago I started adding new command line options to enable/disable warnings at expand–time. I want Vicare’s command line interface to feel like the GCC’s one, so there is a command line option -Wall that will enable all the warnings. Here some other options:

-Wlogic-constants
-Wno-logic-constants

Enable or disable raising a &warning exception when an operand in a logic expression (if test, and, or, et cetera) always returns false or non–false. By default this warning is disabled. As example, the following code:

(and #t (read))

will cause the following warning at expand–time:

*** Vicare: warning:
 Condition components:
   1. &expand-time-type-signature-warning
   2. &who: and
   3. &message: "expression used as operand in logic predicate is \
                 typed as always returning non-false"
   4. &syntax:
       form: #[syntax expr=(and #t (read))
                  line=8 column=3 source="demo.sps"]
       subform: #[syntax expr=#t line=8 column=9 source="demo.sps"]
   5. &type-signature: #[signature (<true>)]
-Wnot-returning
-Wno-not-returning

Enable or disable raising a &warning exception when an operand evaluated for its return values is typed as not returning. By default this warning is disabled. As example, the following code:

(define (operand)
  (error #f "bad behaviour"))

(define (operator rand)
  (display rand))

(operator (operand))

will cause the following warning at expand–time:

*** Vicare: warning:
 Condition components:
   1. &expand-time-type-signature-warning
   2. &who: chi-closure-object-application
   3. &message: "expression used as operand in procedure application \
                is typed as not returning"
   4. &syntax:
       form: #[syntax expr=(operator (operand))
                  line=15 column=3 source="demo.sps"]
       subform: #[syntax expr=(operand)
                     line=15 column=13 source="demo.sps"]
   5. &application-operand-signature: #[signature <no-return>]
-Wunused-variables
-Wno-unused-variables

Enable or disable raising a &warning exception when a local lexical variable is defined but never used. By default this warning is disabled. As example, the following code:

(let ((a 1)
      (b 2)
      (c 3))
  (list a c))

will cause the following warning at expand–time:

*** Vicare: warning:
 Condition components:
   1. &warning-unused-lexical-variable
   2. &who: let/checked
   3. &message: "unused lexical syntactic binding"
   4. &syntactic-identifier: #[id b line=9 column=10 source="demo.sps"]

We need to know that a &warning exception will not stop expansion and compilation, but we must handle it correctly when, for example, we expand code using eval; this is a way of doing it:

(with-exception-handler
    (lambda (E)
      (unless (warning? E)
        (raise-continuable E)))
  (lambda ()
    (eval ?sexp ?env)))

Should eval block &warning exceptions automatically? With a configurable run–time option? Maybe. And maybe, in future, it will.

Experimental overloaded functions

Overloaded functions are an expand–time aggregation of functions linked to the same name and having different number and type of arguments. For example, in the code:

(define/overload (fun {O <fixnum>})
  (list 'fixnum O))

(define/overload (fun {O <string>})
  (list 'string O))

(define/overload (fun {A <vector>} {B <vector>})
  (list 'vectors (vector-append A B)))

(fun 123)               ⇒ (fixnum 123)
(fun "ciao")            ⇒ (string "ciao")
(fun '#(1) '#(2))       ⇒ (vectors #(1 2))

an overloaded function named fun is defined by the first use of define/overload; subsequent uses add new specialisations to the same function. We can use overloaded functions only with the “canonical” function application syntax:

(?fun ?operand ...)

using apply will cause an expand–time syntax violation.

Overloaded functions are also partially supported by records, using the method/overload clause:

(define-record-type <duo>
  (fields one two)
  (method/overload (doit {O <duo>})
    (+ (.one O) (.two O)))
  (method/overload (doit {O <duo>} {C <number>})
    (* C (+ (.one O) (.two O)))))

(define O
  (new <duo> 1 2))

(.doit O)       ⇒ 1
(.doit O 3)     ⇒ 9

Being an expand–time thing: overloaded methods are not available for late binding; that is why support is only partial. This is an important limitation: it makes it harder to implement interfaces (which will require late binding).

At present, only a basic implementation of overloaded functions is available. There are two main reasons:

Now, Vicare already has some sort of solution for these problems: it is implemented by multimethods from the library (vicare language-extensions multimethods). There are problems though:

Solving these problems is one of the things I think of these days…