[racket-dev] When is it safe to not rename a runtime value in a macro?

From: Ryan Culpepper (ryan at cs.utah.edu)
Date: Sat Aug 25 12:53:31 EDT 2012

On 08/25/2012 12:19 PM, Neil Toronto wrote:
> A lot of macros start by renaming some syntax that's assumed to
> eventually represent a runtime value, like the `or' macro does:
>
>  > (syntax->datum (expand-syntax #'(or #t #f)))
> '(let-values (((or-part) '#t)) (if or-part or-part '#f))
>
> But it's not always a good thing, particularly when the output will be
> processed by another macro, and parts will be tested using an
> `identifier=?' function. For example, my `inline-sort' takes `<' as an
> argument. So
>
>    (inline-sort < a b)
>
> expands to
>
>    (let ([lt? <] [temp1 a] [temp2 b])
>      (if (lt? temp1 temp2) (values temp1 temp2) (values temp2 temp1)))
>
> Well, it did at first. When I discovered that Typed Racket's optimizer
> would never detect that `lt?' is actually `<' and replace it with
> `unsafe-fl<', I altered the macro to detect when the `<' is an
> identifier, and not rename it with a `let'.

Is it feasible to make the TR optimizer smarter about this? That would 
be the best solution.

> I know that I should assert my rights under the Macro Bill. But I like
> to program defensively. So what's safe to not rename? I think the
> following are:

I've reordered these a bit:

> number
> string
> bytes
> character
> regexp

In other words, "literal data". But did you check that the '#%datum' 
macro associated with them has the standard meaning? If not, they could 
expand into arbitrary expressions (possibly with side effects)!

> symbol

Do you mean identifier, as in "it's just a variable reference"? But it 
could be an identifier macro. Or it could be a variable that other parts 
of the code could concurrently mutate. For example, suppose 'match' 
expanded less cautiously than it actually does:

   (match x [(cons a b) a])
   =>
   (if (pair? x) (unsafe-car x) (match-error ....))

Now suppose that x is a module-level variable that some other thread 
mutates between the 'pair?' check and the 'unsafe-car'.

> null
> prefab struct key

I don't know what you mean by these in this context. Maybe 'quote' 
expressions containing them?

--

If you must do such things, the safest way in general is to fully 
local-expand the expression you want to analyze; that gives you 
something with a known grammar. Now it's feasible to identify literal 
data: it's any 'quote' expression. You don't have to worry about 
identifier macros, but the 'set!' danger remains for any variables that 
you don't completely control the scope of.

In your specific case, it would also be safe to check whether the 
comparison expression is an identifier free-identifier=? to one of a 
fixed list of known (immutable) variables; if so, it's safe to duplicate.

> Also, should I ever have to intern syntax that's one of these kinds of
> things?

Do you mean intern the data? IIUC, yes: the reader interns literal data, 
but macros are free to introduce non-interned literal data. (It will 
probably get interned again when the compiled form is loaded from zo, 
but there's a period during the expansion of the enclosing module when 
it won't be interned.)

Ryan


Posted on the dev mailing list.