Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix macro hygiene when calling the macro in the same module. #15850

Merged
merged 3 commits into from
Dec 8, 2016

Conversation

yuyichao
Copy link
Contributor

There is a surprising number of base code that relies on the wrong behavior....

One broken test/code that need to be fixed on this commit is the MacroGlobalFunction test. The test doesn't work if the macro is used outside the module so I think the fix is correct. However, should the macro expander detect this case and avoid modifying the function name that is defined to be global already?

Closes #14893

c.c. @JeffBezanson about the test mentioned above and since the code was added a long time ago (605bc65) I can't really find a explanation why the check is necessary.

@yuyichao
Copy link
Contributor Author

Also, the NULL check was added in 7de4648#diff-ee48e36e4d097c33660f778b57339696L97. From the code it seems that module should never be NULL and when I put an abort there it never triggers when running the whole test. Why is the NULL check there?

@JeffBezanson
Copy link
Member

The NULL check might be left over from a case that used to happen during bootstrap.

@yuyichao
Copy link
Contributor Author

OK, I comment out that code (replaced it with an assert) and updated the comment in macroexpand.scm. Let's see if the CI is happy about it...

@JeffBezanson
Copy link
Member

should the macro expander detect this case and avoid modifying the function name that is defined to be global already?

No, it should apply normal hygiene rules, but this is going to require some further fixes. For example in 0.4:

julia> module Foo
               macro m()
                quote
                 global x
                 x = 2
                end
               end
            end
Foo

julia> macroexpand(:(@Foo.m))
quote  # none, line 4:
    global Foo.x # none, line 5:
    Foo.x = 2
end

This is correct, but gets a invalid syntax in "global" declaration error. On master there seems to be a regression, since we get

julia> macroexpand(:(@Foo.m))
quote  # none, line 4:
    global x # none, line 5:
    #2#x = 2
end

I can see why this regression was not caught, since the code doesn't work either way.

Your change to ast.c is fine, but changing the test should not have been necessary. We'd need the macro expansion to return a more permissive kind of module reference that lets you define a new function using the syntax M.f(x) = x. Currently that gives f not defined. As it is, this change will break code that does something like

macro m()
    quote
        global f
        f(x) = 2
    end
end
@m

which would include any module that uses macros to define its own functions. This might be too breaking.

@yuyichao
Copy link
Contributor Author

What do you think is the best solution for this? Allowing assign to m.x (including method definition) if m is current module?

@JeffBezanson
Copy link
Member

Maybe we should just go ahead and allow mod.x = y generally.

@yuyichao
Copy link
Contributor Author

I'm also thinking maybe we can avoid replacing f with M.f if there's global f? This way we can make sure :(global a; a = 1) behaves the same as :(global a = 1)?

@JeffBezanson
Copy link
Member

I think we will still need to return M.f to make sure the correct module is used.

@yuyichao
Copy link
Contributor Author

the correct module is used.

Currently if a macro returns :(global a = 1) it creates a global variable a in the calling module and if :(global a; a = 1) it is an error. I find it a little weird that this two have different behaviors....

@yuyichao yuyichao force-pushed the yyc/macro-curmodule branch 3 times, most recently from a9ed484 to 3f08f8e Compare June 1, 2016 13:08
@yuyichao
Copy link
Contributor Author

yuyichao commented Jun 2, 2016

This PR should be ready now. Before someone restarted AppVeyor there was a timeout (that was not just hanging after the tests are done) possibly due to hitting the swap or something similar.

This still have different behavior for global a = 1 vs global a; a = 1 in a macro from a different module (the first one defines a in caller module and the second defines a in macro defining module).

(jl_value_t*)m, b);
if (jl_expr_nargs(ex) == 1)
return gf;
} else if (jl_is_symbol(fname)) {
jl_value_t **bp=NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote a more complete diff for this file while attempting to do something else:
https://gist.github.com/vtjnash/3775a58db1efe7489e5922ad1eb117f3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the handling in codegen.cpp also needs to be updated for this case

(rr (if (or (symbol-like? rhs) (atom? rhs)) rhs (make-ssavalue))))
`(block
,.(if (eq? rr rhs) '() `((= ,rr ,(expand-forms rhs))))
(call (core setfield!) ,a (quote ,b) ,rr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be valid to lower this line to (= ,lhs ,rr). that should save us from needing to change the meaning of setfield. it looks like it should be handled correctly since #10403

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What commit are you talking about? #10403 is an issue not a PR and all commits referenced in that issue are older than this one and I'm pretty sure $(GlobalRef(...)) = ... is not handled anywhere when I wrote this.

@yuyichao yuyichao force-pushed the yyc/macro-curmodule branch 4 times, most recently from 2315a4d to e6a42ba Compare December 6, 2016 22:52
@yuyichao
Copy link
Contributor Author

yuyichao commented Dec 7, 2016

Rebased on top of master and Jameson's interpreter.c change. CI passed.

Also moved the handling of GlobalRef assignments fully to the runtime/JIT (needed to fix defined-julia-global) so setfield! on the module doesn't need any change anymore.

There's still a difference between global a; a = 1 and global a = 1 but that's not new and is a related but different issue so it doesn't have to be addressed in this PR.

@yuyichao yuyichao merged commit 25e3712 into master Dec 8, 2016
@tkelman
Copy link
Contributor

tkelman commented Apr 22, 2017

This could use a NEWS mention given how commonly macros were hitting this.

@tkelman tkelman added the needs news A NEWS entry is required for this change label Apr 22, 2017
Sacha0 added a commit to Sacha0/julia that referenced this pull request May 12, 2017
Sacha0 added a commit to Sacha0/julia that referenced this pull request May 12, 2017
@Sacha0 Sacha0 removed the needs news A NEWS entry is required for this change label May 13, 2017
tkelman pushed a commit that referenced this pull request May 14, 2017
@andreasnoack andreasnoack mentioned this pull request Jun 29, 2017
tanmaykm added a commit to JuliaCloud/AWS.jl that referenced this pull request Oct 11, 2017
gustafsson pushed a commit to gustafsson/AWS.jl that referenced this pull request Jan 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Macro hygiene only protect you in different modules.
5 participants