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

Method not found when shadowing function from base with a local variable #11723

Closed
mschauer opened this issue Jun 16, 2015 · 16 comments
Closed

Comments

@mschauer
Copy link
Contributor

I have the strange error

ERROR: LoadError: MethodError: `convert` has no method matching convert(::Type{SDE.MvLinPro}, ::Array{Float64,2}, ::Array{Float64,1}, ::Array{Float64,2})
This may have arisen from a call to the constructor SDE.MvLinPro(...),
since type constructors fall back to convert methods.
Closest candidates are:
  SDE.MvLinPro(::Array{Float64,2}, ::Array{Float64,1}, ::Array{Float64,2})

I don't really now where to start, but can this be a reasonable message at all? Julia knows and doesn't know about the inner constructor method

  SDE.MvLinPro(::Array{Float64,2}, ::Array{Float64,1}, ::Array{Float64,2})
@nalimilan
Copy link
Member

Could you provide a reproducer? Maybe an issue with inner vs. outer constructors?

@mschauer
Copy link
Contributor Author

I could boil it down to

module M
export T

type T 
    function T(beta::Int) 
        new()
    end
end

end
using M


for d  = 1:1
    global beta
    beta = 1
    P = T( beta)
end
Warning: imported binding for beta overwritten in module Main
ERROR: LoadError: MethodError: `convert` has no method matching convert(::Type{M.T}, ::Int64)
This may have arisen from a call to the constructor M.T(...),
since type constructors fall back to convert methods.
Closest candidates are:
  M.T(::Int64)
  call{T}(::Type{T}, ::Any)
  convert{T}(::Type{T}, !Matched::T)
 in anonymous at no file:17
 in include at ./boot.jl:254
 in include_from_node1 at loading.jl:133
 in process_options at ./client.jl:304
 in _start at ./client.jl:404
while loading /mnt/sda2/schauer/repro/testlinproc.jl, in expression starting on line 14

Seems to have to do that the name beta is already taken

@fcard
Copy link
Contributor

fcard commented Jun 16, 2015

I think I know what the problem is. Consider the following:

type T
  T() = new()
  T(::Int) = T()
  T(::String) = "I should not be here"
end

module M
  const  c = 1.0
  const  s = ""
  const  i = 1
  global m = ""
end
import M: c,s,i,m

fc() = (global c = 1; T(c))
fs() = (global s = 1; T(s))
fi() = (global i = 1; T(i))
fm() = (global m = 1; T(m))
julia> fm()
Warning: imported binding for m overwritten in module Main
T()

julia> fi()
Warning: imported binding for i overwritten in module Main
T()

julia> fs()
Warning: imported binding for s overwritten in module Main
"I should not be here"

julia> fc()
Warning: imported binding for c overwritten in module Main
ERROR: MethodError: `convert` has no method matching convert(::Type{T}, ::Float64)
This may have arisen from a call to the constructor T(...),
since type constructors fall back to convert methods.
Closest candidates are:
  call{T}(::Type{T}, ::Any)
  convert{T}(::Type{T}, ::T)
  T()
  ...
 in fc at none:1

The compiler doesn't realize that c, s and i are no longer the constants it thought they were, so it continues to use their type information. It thinks that c is a Float64, and as there is no method T(::Float64), it falls back to conversion. It thinks that s is a String, so it substitutes the call to the value of T(::String). It does the same with i, substituting it to T(), assuming i is an Int (it being right is mere coincidence). It does nothing with T(m), as it has no information about m, it being a global.

So yeah, the compiler needs to know when constants are overwritten.

@nalimilan
Copy link
Member

That's the point of constants: declaring you won't modify them to allow the compiler to write efficient code. It would be cool to have a mechanism to recompile the functions when the constant is modified, but AFAICT that's an expected behavior for now.

But I don't see any use of constants in the original issue, so how is that related?

@fcard
Copy link
Contributor

fcard commented Jun 16, 2015

beta is a generic function, so it's a constant.

But you're probably right. I was thinking for a second that maybe it would be useful to let users overwrite imported constants, but then I realized that no, it wouldn't :P Should there be an error for when the import being overwritten is a constant, as it causes this bug?

Then again, most import overwrites are constants, almost forgot what I just said. Iunno.

@fcard
Copy link
Contributor

fcard commented Jun 16, 2015

Also, I just tested, and constants are overwritten when globals with their names are defined, just their type information that remains unaccounted for:

type T
  x::Int

  T(x::Int) = new(x)
  T(x::FloatingPoint) = T(Int(x) + 10)
end

module Const
  const m = 1.0
end
import Const.m

f() = (global m = 0; T(m))
julia>T(0)
T(0)

julia>T(1.0)
T(11)

julia> f()
Warning: imported binding for m overwritten in module Main
T(10)

f calls the method that matches the type of Const.m, but passes the value of Main.m.
That can't be right.

@mschauer
Copy link
Contributor Author

So this is a bug.

@fcard
Copy link
Contributor

fcard commented Jun 19, 2015

I suppose so. I think it makes sense to be able to overwrite imported constants, at least in REPL experimentation, but if that is not desired it should at least throw an error, as it does if one tries to modify a module-local constant with a value of different type, or at least warn about the risks of constant overwriting. By the way, did you manage to work around the problem? Changing the variable name solved it?

@mschauer
Copy link
Contributor Author

mschauer commented Sep 3, 2015

I circumvented it. Could i get this tagged please, presumably with a bug tag, if you agree that it is a bug?

@mschauer mschauer changed the title Method not found / unclear error message Method not found when shadowing function from base with a local variable Sep 3, 2015
@mschauer
Copy link
Contributor Author

Ping.

@mschauer
Copy link
Contributor Author

Maybe @vtjnash is the right person to ping?

@mschauer
Copy link
Contributor Author

Under master f() from above fails with

julia> f()
WARNING: imported binding for m overwritten in module Main
ERROR: error compiling f: Unsupported Float Size
 in eval(::Module, ::Any) at ./boot.jl:230

@ihnorton
Copy link
Member

@mschauer I saw you mentioned this issue on gitter. The issue here is that type-inference operates on the bindings that exist when type-inference is run. Type-inference adds a type-assert that the argument beta is convertible to type M.T. Note that running the for loop a second time (or redefining the function) works fine, because the binding has then been updated before the code is type-inferred (look at the output of @code_typed before/after re-binding beta).

I don't think this is considered a bug; overwriting global bindings at the REPL is allowed for convenience, but that warning is intended to be meaningful as a warning.

See also #7862 #14055 (as well as 265 and 8870)

@mschauer
Copy link
Contributor Author

Thanks, that makes it very clear.

@martinholters
Copy link
Member

I think this has never really been fixed, see #22525.

@martinholters martinholters reopened this Jun 27, 2017
@mschauer
Copy link
Contributor Author

I think this is fixed now, e.g.

julia> f()
ERROR: cannot assign variable Const.m from module Main

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 a pull request may close this issue.

5 participants