[MLton] Implementing warnExnMatch

Stephen Weeks MLton@mlton.org
Wed, 27 Jul 2005 11:11:05 -0700

Sorry for joining the party late, but I find the current specification
and implementation of warnExnMatch a bit ad-hoc.  Let me propose a
different approach.  What "warnExnMatch false" should mean is to
eliminate all nonexhaustive match warnings that arise solely from
unmatched exceptions.  Under this interpretation, all of the
previously dropped warnings would still be dropped.  But so would some
additional ones.  For example, both of the following would *not* give
warnings when compiled "warnExnMatch false" (assume "exception A" has
been declared throughout this mail).

  val A = A and _ = 5
  fun cause A x = ()

This is different than what was done, but I believe makes more sense.
Further, both of the following snippets also would *not* give warnings
when compiled "warnExnMatch false".

  val (A, _) = (A, 13)
  datatype t = T of exn
  val f = fn T A => 13

The thinking here is the same -- the only reason for the nonexhaustive
warning in both cases is that an exception is unmatched.

Finally, there are some cases where a warning would change
("decrease"), but would not be eliminated.  For example

  fun f (A, 3) = ()

With the default annotations, this would give

  Warning: z.sml 2.1.
    Function is not exhaustive.
      missing pattern: (e, _) | (A, 2)
      in: f (A, 3) = ()
The message indicates that there are two reasons why the match is
nonexhaustive, the first being the unmatched exception and the second
being the unmatched integer (with the exception matched).  Compiling
this program with "warnExnMatch false" would give

  Warning: z.sml 2.1.
    Function is not exhaustive.
      missing pattern: (A, 2)
      in: f (A, 3) = ()

That is, the unmatched constant remains but the unmatched exception is

Finally, warnExnMatch has no impact on redundancy warnings.  So,
compiling the following *would* give a redundancy warning.

  val f = fn A => 1 | A => 2

Overall, I like this approach better, so I went ahead and committed an
implementation of it.  The implementation is cleaner as well, I think.
It pushes the information about warnExnMatch past elaborate-core, down
to where the match compile happens, so it happens per-match-compile
instead of per-CoreML-dec.  I had to tweak the match compiler a little
to keep track of whether each example of a nonexhaustive match
depends solely on exceptions, but it wasn't hard.

A couple of other notes.

> BTW, it is useful to note that Type.isExn will return false if the
> type is a type variable being used for inference.  However, this is
> o.k.  Since we will always be checking the type of the scruitinee in
> a pattern match, either the constructors in the pattern will be
> manifestly exception constructors, or they will not.  Hence, the
> type of the scruitinee will always be an exception in the cases
> where we want to turn the match warning off.

This sort of thing scares me a little.  Fortunately, with my approach,
the test for exception types occurs in the CoreML, after type
inference is complete.

> I will note that SML/NJ raises a hard error in the case of 
> a redundant rule, so it is not even possible to compile code with 
> redundant rules.  

I do not see this with SML/NJ 110.54.  It appears to give a warning,
not an error.

> The MLKit gives a warning.  HaMLet does not give a redundant match
> warning.

That looks like a bug in HaMLet?

To round out the list, Poly/ML also gives the redundancy warning.