[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Issue: DECLARE-TYPE-FREE (Version 6)



    Date: Wed, 19 Oct 88 14:56:28 EDT
    From: jar@void.ai.mit.edu (Jonathan Rees)

    [A]   ... Clarify that a type
	  declaration means that it is an error for the value of the variable not
	  to be a member of the declared type, within the scope of the declaration.

    [B]   Clarify that the above programs are valid, and that this  kind of 
	  declaration means the same thing as wrapping a THE form around every 
	  reference to the variable, including modifying references by setq or
	  setf.
	  Clarify that if nested type declarations refer to the same variable, then
	  the value of the variable must be a member of the intersection of the 
	  declared types.

If you take "A" to refer to "lexical scope", I see no serious inconsistency here.
Some minor wording to be dealt with. Probably it should say something to the effect
of "...for an access to this variable to occur in the lexical scope and for the
resulting value to not to be of the indicated type".

The point of this declaration was not to enable the creation of a specialized home
for the variable, so technically it's no trouble for non-matching values to come
and go through mechanisms such as you cite below. The point of this declaration was,
rather, to permit operations which occur on the contents of the variable within the
lexical scope to be interestingly optimized. eg,
 (COND ((SYMBOLP X) 
	;; You might wish the compiler could guess this one -- and some compilers
	;; might, but some not.
	(LOCALLY (DECLARE (SYMBOL X))
	  (STRING X))) ;Could presumably compile down to (SYMBOL-NAME X).
       ((PRIME-INTEGER-P X)
	;; I hope you wouldn't expect a compiler to ever guess this one.
	(LOCALLY (DECLARE (INTEGER X))
	  (ZEROP (- X 7)))) ;Could presumably compile down to (EQL (- X 7) 0)
       ((AND (PATHNAMEP X) (FROBBABLE X))
        ;; Again I hope you wouldn't expect a compiler to ever guess this one.
	;; Maybe FROB-PATHNAME only returns a PATHNAME if its argument is
	;; a frobbable pathname. It's hard to imagine having a way to declare
	;; that and it would be a violation of modularity for a compiler to
	;; notice the fact and compile it into another function, yet it still
	;; might be something a programmer could reasonably "just know" and
	;; so declaring it could be important.
	(LOCALLY (DECLARE (PATHNAME X))
	  (SETQ X (FROB-PATHNAME X))
	  ;; Note that PATHNAME-NAME is defined in CLtL to work on other
	  ;; types than just pathnames, though we could imagine a more
	  ;; efficient access being done here if we knew X was going to hold
	  ;; a real pathname.
	  (PATHNAME-NAME X))))
You could insist that the writer should make a new variable and type-declare
the new variable, but that is contrary to the programming style of many CL
programmers and hence not a practical solution. It also doesn't work for the
case of special variables (mentioned below).

    The phrase "within the scope of the declaration" in [A] is in direct
    conflict with the explanation that follows [B].  If "scope" means
    "lexical scope", then the following would be an error according to [A]
    but not according to [B]:

	    (let ((x 12) (y 'foo))
	      (flet ((zap () (rotatef x y)))
		(locally (declare (fixnum x))
		  (zap)
		  (zap)
		  x)))

Not so. The X referenced by ZAP is not in the lexical scope of the locally
declaration. As such, this program is not in error.

    Here the declaration is violated at the "lexical" point in the program
    between the two calls to ZAP.

This is the "If a tree falls in a forest and nobody hears it" problem. If
you had referred to X in that interim, your program would be in error.
Because you have not, your program is not in error.

    If "scope" means "dynamic scope", 

It does not.

    In either case, this seems like a tricky thing for a compiler to
    attempt to understand, and also pretty tricky for an interpreter (and
    in particular, a formal semantics) to enforce.  I would think that
    recovering the needed information via data flow would be easier.

The intent of the declaration is not to force anyone to detect all cases.
The idea is to permit a compiler to make use of that information if it can.

    Even if you decide to trash definition [A] and go with definition [B],
    I would think that you'd additionally want to specify that the
    declaration must hold not only at all references and assignments
    lexically in the scope of the declaration, but also at the point of
    the declaration itself.

This seems reasonable. It would permit compilers to internally rewrite
the LOCALLY as a (LET ((X X)) ...) for its own notational convenience 
when it could prove that didn't affect other semantics (eg, SETQ would
generally thwart such a rewrite).

    Consider the case where the variable is
    unreferenced in the body; then the declaration wouldn't tell you a
    thing, and e.g. it wouldn't help you with backward flow analysis.

I don't have any objection to this sort of thing happening, but it wasn't
our purpose to permit this. But even if you can't infer stuff backward,
that's not such a big deal. At least the person could extend the LOCALLY
backward if he cared. Right now you can't locally declare type information
at all, so isn't what we're proposing an improvement?

    Or
    the variable might be referenced, but the declaration might not become
    valid until some time a while after the point of the declaration:

	    (let ((x 'foo))
	      (flet ((zap () (setq x 17)))
		(locally (declare (fixnum x))
		  (zap)
		  x)))

Right. This is a case where your restriction that it have that type going
in would help. Then the compiler could internally rewrite it as
(LET ((X X)) (DECLARE (FIXNUM X)) ...).

    Also, what does the declaration mean for non-lexical variables?  Would
    it apply dynamically to any dynamic binding or reference in the
    dynamic scope of the declaration, or just to the references to the
    particular dynamic binding that was in effect at the point of
    declaration, or just to lexically apparent references in the lexical
    scope of the declaration?

The declaration itself is lexically scoped -- independent of the scope of
the variable.

    I would strongly oppose the proposal in its current vague form.

Thanks for the criticisms. I'm sure they'll be helpful in tightening things
up.

    I think I would oppose the proposal in just about any form, because it
    makes the semantics of variable references even more hairy than it
    already is.

This would be sad. Right now we have no way to do local declarations other
than at the time a variable binding is instantiated and we're proposing to
make the situation somewhat better. Some of your criticisms sound like 
they're saying we're not solving the whole problem (which we're not) and
so we're doing no good. In fact, I think that solving half the problem will
make serious strides toward people's being able to write programs which
are concise and efficient.

    Compilers ought to recover this kind of information from
    data flow analysis, assisted if necessary by assertions (e.g. uses of
    THE in for-effect positions).

That would be cute but not very perspicuous. There's no difference between
  (LOCALLY (DECLARE (FIXNUM X)) (+ X X))
and
  (PROGN (THE FIXNUM X) (+ X X))
other than that the former is clumsy and earthy-sounding. The latter is like
saying:
  "The accountant... Joe... Joe married Sally."
when you want to say:
  "Joe, who is an accountant, married Sally."
It gets the information across, but not in a way that makes it clear what
you were trying to say.