Standard ML of New Jersey
Version 110.9, October 19, 1998

This version consist primarily of enhancements to CM and changes to the module system.


Bug Fixes

Relative to 110.8

No. Description
1352 Equality status of reals is compromised
1354 spurious? "possibly inconsistent structure definitions"
1357 rebinding of constructors and exceptions not allowed
1364 Mistakenly accepted datatype spec when signature matching.
1374 Datatype replication causes nonexhaustive match error.
1384 incorrect complaint about "inconsistent structure definitions"
1385 functor defn including "where" structure defs shouldn't elaborate
1386 redefinition of a type spec is not detected
1414 Compiler bug: Instantiate: unexpected DATATYPE 354
1417 problems with datatype replication in functors
1418 CM.set_path is a constant function
1421 incorrect type comparison for val spec in signature match
1422 Core dump on Sparc when using lazy features
1423 2.0 + 2.0 = nan
1426 smlnj-c interface function, second call core dumps
1428 CM docs out of date
1432 signature match fails for datatype specs if "where type" is used
1433 eqtype u=t doesn't force eqtype. [partial fix]
1438 Wrong types for TextIO.StreamIO.inputAll and TextIO.StreamIO.mkInstream
1440 CM.set_path has no effect in Win 32 or Irix 6.4
1445 uncaught Unbound in FLINT/trans/transtypes.sml
1450 bugs in Array2.fromList and Array2.row
-- fixed Real.fromString (regression test failures in basis/tests/reals.sml)


Module System

1. Layered Redefinitions Ignored (Bug 1354 fixed)

Structure definition specs can easily give rise to redefinitions. Consider the following example:

  signature S1 =
  sig
    type t
  end;

  signature S2 =
  sig
    structure A : S1
    structure B : S1 = A
  end;

  signature S3 =
  sig
    structure C : S2
    structure D : S2 = C
  end;
Here the substructure D.B of S3 is defined within S2 in terms of D.A (implying D.B.t = D.A.t), while by the definitional spec in S3, D.B is equal to C.B (implying D.B.t = C.B.t).

There are two ways of dealing with such secondary, or layered definitional specs.

  1. Secondary definitions can be treated as errors (excepting those cases where the equivalence of the definitions can easily be verified). This was the policy for 110.0.3 through 110.8, where examples like the above gave rise to the error message
            Error: possibly inconsistent structure definitions
          
    [This was bug 1354.]
  2. The redefinitions can be regarded as producing implied sharing constraints (e.g. D.A.t = C.B.t in this case).
In case (2), these implied sharing constraints will be verified automatically during signature matching, in the process of matching all the definitional specifications. The question is whether they should be taken into account (i.e. satisfied) when instantiating a signature like S3. We adopt the lenient policy of not dealing with these implied constraints during instantiation. Instead, during instantiation secondary definitions are simply ignored. In the example, the inner definition D.B.t = D.A.t takes effect while the secondary definition D.B.t = C.B.t is ignored.

The consequence of this policy is that some additional inconsistent signatures will be successfully instantiated. As usual, any attempt to match these inconsistent signatures will fail.

The compiler flag

  Compiler.Control.multDefWarn : bool ref
controls whether a warning message will be generated when a secondary definition is ignored. It's default value is false, meaning no warning messages will be produced.

We follow a stricter policy in some other cases, such as layered type definition specs:

  signature S = sig
    type t
    type s = t
  end where type s = int
Here the secondary redefinition "where type s = int" is detected and causes an error message.
  Error: where type defn applied to definitional spec: s
NOTE: This is an SML/NJ divergence, since the above signature is legal in SML '97. See Note "Sharable Types" below.

2. Noninterference of sharing and definitional specs

While the above solution relaxed a constraint enforced by the compiler, this problem involves enforcing a new constraint relating to the interaction between sharing and definitional specs.

The Definition requires that a type constructor involved in a sharing constraint be (1) not defined as a type function, and (2) not defined in terms of some "rigid" type constructor (i.e. a type constructor previously defined in the context).

We choose to define "sharable" as meaning simply that there is {\em no} definition applying to a type constructor. We'll use the term "defined" for the opposite of sharable. A more subtle definition is possible; see note "Sharable Types" below.

Thus the following signature is illegal

  signature S =
  sig
    type s = int  (* s is defined *)
    type t        (* t is sharable *)
    sharing type t = s  (* s is not sharable *)
  end
and has to be reexpressed as (for instance):
  signature S =
  sig
    type s = int
    type t = s
  end
With "where type" definitions, things are a little more complicated. An inner type sharing constraint can be affected by an outer definitional constraint, as in the following example:
  signature S1 =
  sig
    type s
    type t
    sharing type t = s  (* ok, because s and t are sharable here *)
  end
  where type t = int;  (* this converts both s and t to rigid types *)
This is legal, but the following declaration is not:
  signature S2 =
  sig 
    structure A : S1
    type v
    sharing type v = A.s  (* A.s not flexible *)
  end;
However, S2 can easily be converted to the legal S3 below by replacing the outer sharing constraint by a definition.
  signature S3 =
  sig 
    structure A : S1
    type v = A.s
  end;
In general, we recommend avoiding sharing constraints that can easily be expressed by definitional specs. So one should always prefer
  type t = s
to
  type t
  sharing type t = s.
The same applies to structure sharing and structure definition specs (which are an SML/NJ language extension). Violations of this newly enforced constraint can often be eliminated by replacing structure sharing by structure definition specs, e.g. replacing
  structure A : SIGA
  sharing A = B.C
with
  structure A : SIGA = B.C.

SML/NJ Exception for Structure Sharing with Same Signature

SML/NJ provides one important exception to the rule about sharing rigid types. This is the case where the type sharing is implied by structure sharing between two structures with the same signature.

Here is an example

  signature S =
  sig
    type t = int
  end

  signature S1 =
  sig
    structure A : S
    structure B : S
    sharing A = B
  end
This is allowed in SML/NJ because A and B have the same signature, even though the sharing constraint is equivalent to
  sharing type A.t = B.t
and A.t and B.t have the rigid spec
  type t = int.

Note: Sharable Types

[Mostly for language lawyers]

There is some controversy about what type constructors should be allowed in sharing constraints. We can illustrate this by the following example

  signature S =
  sig
    type s
    type t = s
    type u
    sharing type t = u
  end
By our definition above, t is defined, and therefore not sharable, and this signature declaration is rejected. Technically, however, the semantic representation of t in the signature is the type function \().() ns (a nullary type function, where ns is the semantic type "name" for s), and this type function is eta-equivalent to ns, a simple flexible type name. Therefore, if this eta-reduction is assumed, t meets the requirements of the definition and can appear in the sharing constraint. On the other hand, consider
  signature S =
  sig
    type s
    type t = s list
    type u
    sharing type t = u
  end
Here the representation of t is the type function \().(()ns) list, which does not reduce to a simple type name, so the sharing constraint is clearly illegal. The reasons that we adopt the simpler and more restrictive meaning of sharable are that it is easier to explain and it admits all sensible usages. I claim that it promotes a cleaner and simpler style in signature writing. It is also much simpler more efficient to implement (at least for SML/NJ). Here are some example signatures that are admitted under the more complicated version of the definition (thanks to Martin Elsman):
  signature S1 =
  sig
    type t
    type s = t
  end where type s = int

  signature S2 =
  sig
    type t
    type s = t
    sharing type t = s
  end

  signature S3 =
  sig
    type s
    structure U :
      sig
        type 'a t
        type u = (int * real) t
      end where type 'a t = s
  end where type U.u = int
It is not clear that examples like these have any importance to anyone other than language lawyers. The last is particularly perverse: reading a signature should not be an exercise in puzzle solving!

CM

User Level Changes

COMPILER NOTES

Bootstrap Internals

A bit of History

Originally, when you said CMB.make();, CM would compile the entire source tree and build compilers as well as interactive systems for all available architectures. CM would then output "list" files in the bin.[arch]-[os] directory that omitted those sources that are not necessary for the current architecture.

But still, the name of the Compiler structure as well as the name of the structure representing the interactive system was architecture-specific. Therefore, the boot process would select the bindings for those structures from the environment and re-bind them as "Compiler" etc.

Moreover, CM itself would be built as an "ordinary" SML program functorized by the "Compiler" structure.

The New Organization

Since sources of compiler and CM are merged, some things have already been streamlined. I simplified things further in the following way: