From: Vassil Nikolov
Subject: Summing up: when type mismatch can be legal
Date: 
Message-ID: <76350.vnikolov@math.acad.bg>
Dear Readers,

In a previous article, I asked:

  Is the compiler justified in refusing to compile
    (declare (number x))
    (if (numberp x) (1+ x) (cdr x))
  because of the apparent application of CDR to a non-cons?

The answer seems to be negative; a warning may be fine,
but not an error.  I thank Barry Margolin for his explanation.

(Somewhat to my dismay, this issue was linked to the
halting problem by a subsequent post.)

Please note that without the declaration there would
be nothing to discuss.  A more realistic situation
would be one where the declaration comes from one
part of the program and the branching statement
from another part, possibly from a macro, so
perhaps I should explain how the question arose.
A friend had a problem running DEFSYSTEM.  (I have
summed up his description below.)  While he easily
fixed it, I began wondering which was the culprit.
Apparently, the compiler is to blame, but now
I wish the description of the Common Lisp language
was a little more explicit on this point.
(Of course, I wouldn't really insist on that,
since a language description cannot possibly
be explicit in all respects.)

Best regards,
Vassil.

- - - - -

DEFSYSTEM ver. Mar-95
CMU CL ver. 17f (SunOS 5.4)

The source contains the following definition:

   (defsetf component-load-time (component) (value)
     `(when ,component
       (etypecase ,component
         (string ...)
         (pathname ...)
         (component
          (ecase (component-type ,component)
            (:defsystem ...)
            ((:file :private-file) ...) )))
       ,value))

and, later on:

   (defun find-system ...
       ...
       (setf (component-load-time path) (file-write-date path))
       ...)

Now the PATH variable here is known at compile time to be
a pathname and therefore unsuitable as an argument to
COMPONENT-TYPE (in the ECASE form above).  So the compiler
objects, although that branch of the ETYPECASE would never
be executed to begin with,---compiling the above produces:

  In: DEFUN FIND-SYSTEM
    (SETF (COMPONENT-LOAD-TIME PATH) (FILE-WRITE-DATE PATH))
  --> LET* MULTIPLE-VALUE-BIND LET WHEN COND IF PROGN ETYPECASE LET COND IF COND
  --> IF COND IF PROGN ECASE LET COMPONENT-TYPE
  ==>
    #:G8
  Warning: Result is a PATHNAME, not a COMPONENT.

Here #:G8 comes from the macroexpansion

  (SETF (COMPONENT-LOAD-TIME PATH) ...) ->

  (LET* ((#:G8 PATH))
     ...
     (ETYPECASE #:G8
       (STRING ...)
       (PATHNAME ...)
       (COMPONENT (ECASE (COMPONENT-TYPE #:G8) ...) ...) ...))

The compiler detects type inconsistency and does not compile
the form. (It substitutes it with a stub that calls ERROR at
runtime instead.)

(An obvious remedy that worked was to define a function
whose body was the body of the long form of DEFSETF, and
then use the short form of DEFSETF with the name of the
function.)

- - - - -


Vassil Nikolov         <········@bgearn.acad.bg>     (+359-2) 713-3813
Department of Information Research
Institute of Mathematics and Informatics        fax: (+359-2) 9713649
Acad. G. Bonchev, block 8, Sofia 1113, Bulgaria

From: Raymond Toy
Subject: Re: Summing up: when type mismatch can be legal
Date: 
Message-ID: <4nd8miljhz.fsf@rtp.ericsson.se>
>>>>> "Vassil" == Vassil Nikolov <········@math.acad.bg> writes:

    Vassil> DEFSYSTEM ver. Mar-95
    Vassil> CMU CL ver. 17f (SunOS 5.4)

[snip]

    Vassil>   In: DEFUN FIND-SYSTEM
    Vassil>     (SETF (COMPONENT-LOAD-TIME PATH) (FILE-WRITE-DATE PATH))
    --> LET* MULTIPLE-VALUE-BIND LET WHEN COND IF PROGN ETYPECASE LET COND IF COND
    --> IF COND IF PROGN ECASE LET COMPONENT-TYPE
    Vassil>   ==>
    Vassil>     #:G8
    Vassil>   Warning: Result is a PATHNAME, not a COMPONENT.

    Vassil> Here #:G8 comes from the macroexpansion

    Vassil>   (SETF (COMPONENT-LOAD-TIME PATH) ...) ->

    Vassil>   (LET* ((#:G8 PATH))
    Vassil>      ...
    Vassil>      (ETYPECASE #:G8
    Vassil>        (STRING ...)
    Vassil>        (PATHNAME ...)
    Vassil>        (COMPONENT (ECASE (COMPONENT-TYPE #:G8) ...) ...) ...))

    Vassil> The compiler detects type inconsistency and does not compile
    Vassil> the form. (It substitutes it with a stub that calls ERROR at
    Vassil> runtime instead.)

Why is this a problem?  As you state above, and the compiler warns,
PATH is a PATHNAME at this point, so the COMPONENT part of etypecase
would never be executed.  So the compiler could have put
(blow-up-my-computer) for the COMPONENT case and you couldn't tell at
run time.  (Assuming an error-free compiler, of course!)

Of course, it seems the compiler is a bit deficient since it could
have replaced all of the code for the etypecase with just the
code for the PATHNAME part.

Ray
From: Barry Margolin
Subject: Re: Summing up: when type mismatch can be legal
Date: 
Message-ID: <5v3itk$kq8@tools.bbnplanet.com>
In article <··············@rtp.ericsson.se>,
Raymond Toy  <···@rtp.ericsson.se> wrote:
>Why is this a problem?  As you state above, and the compiler warns,
>PATH is a PATHNAME at this point, so the COMPONENT part of etypecase
>would never be executed.  So the compiler could have put
>(blow-up-my-computer) for the COMPONENT case and you couldn't tell at
>run time.  (Assuming an error-free compiler, of course!)

I think you're misunderstanding which form got replaced with the stub.
Judging from the original post in the thread, I believe it's the entire
ETYPECASE or LET that's being replaced, not just the COMPONENT clause of
the ETYPECASE.  And that's the whole problem -- the compiler detected a
type mismatch, but doesn't recognize the guards surrounding it.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
Support the anti-spam movement; see <http://www.cauce.org/>
Please don't send technical questions directly to me, post them to newsgroups.
From: Raymond Toy
Subject: Re: Summing up: when type mismatch can be legal
Date: 
Message-ID: <4nbu22lei6.fsf@rtp.ericsson.se>
>>>>> "Barry" == Barry Margolin <······@bbnplanet.com> writes:

    Barry> In article <··············@rtp.ericsson.se>,
    Barry> Raymond Toy  <···@rtp.ericsson.se> wrote:
    >> Why is this a problem?  As you state above, and the compiler warns,
    >> PATH is a PATHNAME at this point, so the COMPONENT part of etypecase
    >> would never be executed.  So the compiler could have put
    >> (blow-up-my-computer) for the COMPONENT case and you couldn't tell at
    >> run time.  (Assuming an error-free compiler, of course!)

    Barry> I think you're misunderstanding which form got replaced with the stub.
    Barry> Judging from the original post in the thread, I believe it's the entire
    Barry> ETYPECASE or LET that's being replaced, not just the COMPONENT clause of
    Barry> the ETYPECASE.  And that's the whole problem -- the compiler detected a
    Barry> type mismatch, but doesn't recognize the guards surrounding it.

If that is true, then that is definitely a bug in CMUCL.

However, I've been running this defsystem for quite a while and, while 
I have seen the warnings mentioned above, I have not noticed any thing 
broken.  A short test with some print statements around the offending
code shows that the etypecase or let is not replaced by error; it
works as expected.

Perhaps a newer version of CMUCL will help.  I did the test with an
almost but not quite 18a release of CMUCL.

Ray