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

Obscure issue with redefining functions #4688

Closed
simonster opened this issue Oct 30, 2013 · 8 comments
Closed

Obscure issue with redefining functions #4688

simonster opened this issue Oct 30, 2013 · 8 comments
Assignees

Comments

@simonster
Copy link
Member

julia> b() = false
b (generic function with 1 method)

julia> begin
         a() = 1
         let x = true
           b() = x == true ? a() : a()
         end
       end
b (generic function with 1 method)

julia> b()
1

julia> begin
         a() = 2
         let x = true
           b() = x == true ? a() : a()
         end
       end
b (generic function with 1 method)

julia> b()
1

I wouldn't expect redefining a() alone to make a difference here, since b() is inlining it, but I would expect that redefining a() and then redefining b() should change the output of b(). If I modify b() so that it doesn't use x it works:

julia> begin
         a() = 2
         let x = true
           b() = true == true ? a() : a()
         end
       end
b (generic function with 1 method)

julia> b()
2

If I remove the begin/end around the redefinition it also works:

julia> a() = 2
a (generic function with 1 method)

julia> let x = true
         b() = x == true ? a() : a()
       end
b (generic function with 1 method)

julia> b()
2
@JeffBezanson
Copy link
Sponsor Member

I believe this is caused by b getting compiled along with the expression that redefines a; hence the old version of a is inlined into b.

@simonster
Copy link
Member Author

It seems like b itself isn't getting compiled (I can reproduce even if I give b an argument, which would prevent it from being compiled eagerly) but the whole block gets compiled and the call to a() gets inlined when the block is compiled even though it's inside a closure. code_lowered shows that when b is redefined the old a has been inlined into it before type inference. I suspect this only happens when b actually uses x because capturing the variable forces the block to be compiled instead of interpreted.

@StefanKarpinski
Copy link
Sponsor Member

Since redefining functions isn't officially supported, I'm not sure that this can be considered a bug. This is really just a part if #265.

@simonster
Copy link
Member Author

Yes, since redefining functions isn't officially supported this may not qualify as a bug. But I'm not sure fixing #265 would necessarily fix this. It seems like a gets inlined when the entire block gets compiled, but a doesn't change until the block is already running.

@simonster
Copy link
Member Author

Here's a case that doesn't involve redefining any functions, only defining new methods of the same function before ever calling it:

julia> a(y) = "should be unreachable by calling b"
a (generic function with 1 method)

julia> b(y) = "not an Int"
b (generic function with 1 method)

julia> begin
         a(y::Int) = "an Int"
         let x = true
           b(y::Int) = x == true ? a(y) : a(y)
         end
       end
b (generic function with 2 methods)

julia> b(1)
"should be unreachable by calling b"

@StefanKarpinski
Copy link
Sponsor Member

Ok, yeah, that's definitely a bug. Nice find. You're really putting the type inference through its paces.

@JeffBezanson
Copy link
Sponsor Member

Same theory applies; it is looking at the old version of a. Not really related to type inference.

@ghost ghost assigned JeffBezanson Nov 4, 2013
@JeffBezanson
Copy link
Sponsor Member

Although the behavior here is a bug (since the interior of b should be delayed until it actually runs), we are going to have to specify method definition semantics very carefully. In particular this might have to be allowed:

julia> a()=1

julia> b()=a()

julia> b()
1

julia> begin
        let x=0
          a()=2
          b()=a()+x
          b()
        end
       end
1

julia> b()
2

For example I might specify "adding a method to a top-level generic function is not guaranteed to take effect before the next top-level expression". The effect within the current top-level expression is undefined.

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

No branches or pull requests

3 participants