[MLton] mlb support

Stephen Weeks MLton@mlton.org
Mon, 28 Jun 2004 14:06:14 -0700


> > <ann> ::= showBasis <file>
> >         | showBasisUsed <file>
> >         | showDefUse <file>
> >         | warnUnused
> 
> I would kick showBasis out of this group.  It is not def-use related; in
> particular, the basis defined by a particular bdec remains the same,
> regardless of how the bdec is used.

I agree that showBasis is not def-use related and the basis is
constant, but I still think it makes perfect sense to write

	ann showBasis foo in bdec end

to cause the basis that bdec defines to be written to foo.  And having
an annotation provides finer-grained control than a command-line
switch.

> Also, I'm not keen on the showX <file> annotations; seems weird to
> produce these files just based on the annotations.

I agree it's strange to cause side effects with the annotations. But,
it's not a showstopper to me.  Also, we're already causing elaboration
side effects with the other annotations (allowConst, rebindEquals).

> z.mlb:
> 
>   local
>     basis B = bas  basis-2002.mlb  end
>     basis B1 = bas  ann showBasisUsed a1.basis-used in open B end  end
>     basis B2 = bas  ann showBasisUsed a2.basis-used in open B end  end
>   in
>     local open B1 in a1.sml end
>     local open B2 in a2.sml end
>   end
> 
> I'd like to believe that this will write to a1.basis-used the portion of
> the Basis Library used in a1.sml and write to a2.basis-used the portion of
> the Basis Library used in a2.sml, 

That does seem logical.

> but don't think that will happen with the infrastructure we have. 

Yep.

> I suspect that this will actually write nothing to either of the
> files, because no new decs are introduced by the annotations,
> despite the fact that new bindings are introduced.

For "ann showBasisUsed foo in bdec end", I think it would be pretty
easy to save the basis produced by bdec, then to wait until the
elaboration finishes, walk through the saved basis and write out the
used information.  We could even have showBasisUsed make a copy (in
the sense of def-use information) of the basis that bdec produces so
that we get what you want above.  But I agree that could get messy and
isn't clearly necessary.  So let's keep shooting at something simpler.

Let's see if we can work from the other direction -- starting with
what our infrastructure can easily provide and developing some
annotations with reasonable semantics.

The data our infrastructure stores:

1. With every binding (of a variable, tycon, strid, ...), we keep two
   pieces of information (Uses.t):
   a. a list of all the occurrences where it is used
   b. a bool indicating whether or not the variable may be reported as
      unused via -warn-unused.

2. With each structure component, we keep the same information as with
   the binding that got stored in the structure.

3. For each name space, we keep a list of all the identifiers that
   have been defined (at any scope).

The operations that our infrastructure provides:

1. Env.clearDefUses, which 
   a. for each namespace, sets the list of defined identifiers to [].
   b. for each defined symbol and each binding that occurs in a
      structure, clear the use information.

2. Env.forceUsed, which for each defined symbol and each binding that
   occurs in a structure, set the bool associated with the binding so
   that it cannot be reported as unused.

3. Env.{layout,layoutCurrentScope,layoutUsed}, which are various ways
   of displaying environments.

One natural thing to do is to reify the operations that we have and
make them available in the mlb files.  Something like

bdec ::= clearDefUses
       | forceUsed
       | layout <file>
       | layoutCurrentScope <file>
       | layoutUsed <file>

Then, the expansion of f.sml on the command line becomes:

f.sml	===>	local
		   $(SML_BASIS)/basis-2002.mlb
                   clearDefUses
		in
		   f.sml
		end
                forceUsed

This will cause -warn-unused to only display def-use information for
the user program.

On the other hand, the expansion of no file on the command line
becomes:

<no file>  ==>  $(SML_BASIS)/basis-2002.mlb

Hence, -warn-unused will display def-use information for the basis
library.

One problem with this approach is that Env.clearDefUses don't scope
well.  That is, it clears the namespace definition lists completely,
not just what's in the local scope.  Maybe it would be cleaner to have
something like what I said before

	ann ::= keepDefs {false|true}

With keepDefs false, the elaborator simply wouldn't add bindings to
the namespace lists.  With this, we could expand f.sml to

f.sml	==>	local
		   ann keepDefs false in $(SML_BASIS)/basis-2002.mlb end
		in
		   f.sml
		end
                forceUsed

I guess forceUsed could be an annotation too.

f.sml	==>	local
		   ann keepDefs false in $(SML_BASIS)/basis-2002.mlb end
		in
		   ann forceUsed in f.sml end
		end

>     + suppose I have a pile of code from somewhere, that I want to use
>       in a library.  I'm a principled programmer, so I always compile with
>       -warn-match true & -sequence-unit true.  Unfortunately, the author
>       of the other code wasn't so principled, and I'd love to be able to
>       include their code in a mlb as follows:
> 
>       proj.mlb
>          ann warnMatch false, sequenceUnit false in util.mlb end
>          ann warnMatch true, sequenceUnit true in mycode.sml end

This is easy.  For sequenceUnit, simply keep track of the current
value of the bool in the elaborator and use it instead of
!Control.sequenceUnit when deciding whether to warn in
elaborate-core.fun.  For warnMatch, in the CoreML that comes out of
the elaborator, record the current value of the bool in every Case
expression.  Then, the code that generates the warnings in
defunctorize.fun can use the bool in the Case expression instead of
the global Control value to decide whether or not to warn.

>       Note, I wouldn't expect the warnMatch true and sequenceUnit true
>       annotations to generate warnings if the appropriate options weren't
>       set on the command line.  So, one probably wouldn't use such
>       annotations unless they were buried under a corresponding false
>       annotation.

Dunno.  It might be more natural to have the outermost annotation be
set by the command-line switch, and then have the completely normal
fluidLet behaviour underneath.  So, setting warnMatch true will
*always* cause the warning to be displayed, regardless of the
command-line switch.

>     + I might also develop my code with -warn-unused true, but doing so
>       leads to lots of errors from the old code.  Could we accomodate
>       turning off the warning in the old code?

Sure, I think the keepDefs annotation does this nicely.


Here's our status as I see it.  

-show-basis
	we could only allow it on the command line or we could have it
	as side-effecting annotation.  Neither of those is problematic
	from an implementation perspective.

-show-basis-used
	To support this, we appear to need an annotation like
	layoutUsed.

-show-def-use
-warn-unused
	We have a reasonable solution via the keepDefs annotation,
	which has a simple implementation as a fluidLet bool ref.
	This allows us to recover our old behavior, and to do new
	stuff like selectively disable unused warnings for old code.


I think it would be fine for a first cut to have

ann := allowConst {false|true}
     | rebindEquals {false|true}
     | keepDefs {false|true}
     | sequenceUnit {false|true}
     | warnMatch {false|true}
      
Then, we can implement -show-basis, -show-def-use, and -warn-unused as
command line options.  For now, we can drop -show-basis-used.  Once we
get a better feel for things, we can add in layoutX annotations for
that and possibly for showBasis.