[Openmcl-devel] Weird code
Gary Byers
gb at clozure.com
Sat Sep 11 01:45:30 PDT 2010
The #_ does something like the following (with some
details glossed over):
1) reads a token from the stream, with case preservation.
2) interns that token in the "OS" package (where "OS" is
a nickname for some platform-dependent package.) This
step yields a symbol, OS::|foo| for example.
3) looks up that symbol's name in the interface database.
If it finds a function definition for that name, installs
information about that foreign function's argument and
result types in some FFI data structures; if no definition
was found, signal an error.
4) Set the global macro definition of the OS-package symbol
(OS::|foo|) to an expansion function that uses the info
from step (3) to generate an FF-CALL form where the argument/
result type information is explicit.
So:
(#_malloc 10)
- reads and interns OS::|malloc| (steps 1 & 2)
- looks up "malloc" in the interface database (3) and caches some info
about that function's arg and result types.
- defines OS::|malloc| to be a macro
and (#_malloc 10) is read as (os::|malloc| 10), e.g., an invocation of
the macro OS::|malloc|.
If that macro call is expanded in the same session (as ordinarily happens;
the form will be compiled soon after it's read in most cases), the macro
expands into some variant of:
(external-call "malloc" :int 10 :address)
That all works if the steps performed by the reader macro have been performed
in the current lisp session.
If we try to write a macro that uses a reader-macro in its expansion - something
like:
(defmacro allocate (size)
`(#_malloc ,size))
That'll likely work as expected if we expand that macro in the same session
in which #_malloc was read and the read-time side-effects occurred.
If we compile that macro definition and later load it into a session in which
those read-time side-effects haven't occurred, then we'll likely get an error
to the effect that OS::|malloc| isn't defined.
The weird code that was doing READ-FROM-STRING at macroexpand time was trying
to force those side-effects to occur (at macroexpand time). That makes it possible
to use #_ in the macroexpansion, though as Gail noted it can only work if it's
sure of what readtable's in effect during the READ-FROM-STRING.
(The macro that Ron found probably would be simpler if it expanded
into something like
(external-call "memset" ...)
instead of doing what it's doing. I wouldn't be shocked if the READ-FROM-STRING
idiom was used in a few other places (or if those other places also neglected
to ensure that the expected readtable was in effect.)
People who used MCL may remember something called REQUIRE-TRAP; it existed for
the reasons outlined above and may have been implemented in terms of READ-FROM-STRING.
On Sat, 11 Sep 2010, james anderson wrote:
>
> On 2010-09-10, at 22:25 , Gail Zacharias wrote:
>
>> On Fri, Sep 10, 2010 at 2:27 PM, Ron Garret <ron at flownet.com> wrote:
>>> I happened upon the following odd behavior while mucking with some reader
>>> macros:
>>>
>>> ? (set-macro-character #\m
>>> (lambda (stream c)
>>> (unread-char c stream)
>>> (let ((*readtable* (copy-readtable nil)))
>>> (read stream))))
>>> T
>>> ? 'moo
>>> MOO
>>> ? (defun foo (a b) (#/sizeWithAttributes: a b))
>>>> Error: Foreign function not found: X86-DARWIN64::MEMSET
>>>> While executing: CCL::LOAD-EXTERNAL-FUNCTION, in process Listener(6).
>>>> Type cmd-. to abort, cmd-\ for a list of available restarts.
>>>> Type :? for other options.
>>> 1 >
>>> ? (defun foo (a b) (#/sizeWithAttributes: a b))
>>> ;Compiler warnings :
>>> ; In FOO: In the call to NEXTSTEP-FUNCTIONS:|sizeWithAttributes:| with
>>> arguments (A B),
>>> ; 2 arguments were provided, but at most 0 are accepted
>>> ; by the current global definition of
>>> NEXTSTEP-FUNCTIONS:|sizeWithAttributes:|
>>> FOO
>>> ?
>>>
>>>
>>> I tracked the problem down to the following code in lib/macros.lisp:
>>> [...]
>>> and in particular, to this line:
>>>
>>> (memset (read-from-string "#_memset")))
>>>
>>> Is there a reason why read-from-string being invoked here instead of just
>>> doing, say, (let ((memset '#_memset)) ... ? Or dispensing with the
>>> binding entirely and just doing (,#_memset ...) in the body of the LET?
>>
>> It's so that the side-effects happen at runtime rather than compile
>> time. But it's a bug that it's just using whatever readtable happens
>> to be in force at the time.
>
> but doesn't that bit of code already know what symbol it wants to resolve?
> in which case, the question is, what is the intended benefit running it
> through the reader rather than calling load-external-function directly?
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel
>
More information about the Openmcl-devel
mailing list