diff --git a/NEWS.md b/NEWS.md index d277817a98277a..a31bc5387658f6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,6 +16,9 @@ Language changes * In string and character literals, backslash `\` may no longer precede unrecognized escape characters ([#22800]). + * Declaring arguments as `x::ANY` to avoid specialization has been replaced + by `@nospecialize x`, which needs to be imported from `Base`. ([#22666]). + Breaking changes ---------------- diff --git a/base/array.jl b/base/array.jl index f7dc11f1e04716..bd5cda891903ad 100644 --- a/base/array.jl +++ b/base/array.jl @@ -238,7 +238,7 @@ getindex(::Type{T}, x) where {T} = (@_inline_meta; a = Array{T,1}(1); @inbounds getindex(::Type{T}, x, y) where {T} = (@_inline_meta; a = Array{T,1}(2); @inbounds (a[1] = x; a[2] = y); a) getindex(::Type{T}, x, y, z) where {T} = (@_inline_meta; a = Array{T,1}(3); @inbounds (a[1] = x; a[2] = y; a[3] = z); a) -function getindex(::Type{Any}, vals::ANY...) +function getindex(::Type{Any}, @nospecialize vals...) a = Array{Any,1}(length(vals)) @inbounds for i = 1:length(vals) a[i] = vals[i] @@ -486,9 +486,9 @@ function _collect_indices(indsA, A) end if isdefined(Core, :Inference) - _default_eltype(itrt::ANY) = Core.Inference.return_type(first, Tuple{itrt}) + _default_eltype(@nospecialize itrt) = Core.Inference.return_type(first, Tuple{itrt}) else - _default_eltype(itr::ANY) = Any + _default_eltype(@nospecialize itr) = Any end _array_for(::Type{T}, itr, ::HasLength) where {T} = Array{T,1}(Int(length(itr)::Integer)) @@ -694,7 +694,7 @@ function push!(a::Array{T,1}, item) where T return a end -function push!(a::Array{Any,1}, item::ANY) +function push!(a::Array{Any,1}, @nospecialize item) _growend!(a, 1) arrayset(a, item, length(a)) return a diff --git a/base/associative.jl b/base/associative.jl index d1df6189c2fb79..31c3f3f6cad612 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -461,7 +461,7 @@ function sizehint!(t::ObjectIdDict, newsz) rehash!(t, newsz) end -function setindex!(t::ObjectIdDict, v::ANY, k::ANY) +function setindex!(t::ObjectIdDict, @nospecialize(v), @nospecialize(k)) if t.ndel >= ((3*length(t.ht))>>2) rehash!(t, max(length(t.ht)>>1, 32)) t.ndel = 0 @@ -470,22 +470,22 @@ function setindex!(t::ObjectIdDict, v::ANY, k::ANY) return t end -get(t::ObjectIdDict, key::ANY, default::ANY) = +get(t::ObjectIdDict, @nospecialize(key), @nospecialize(default)) = ccall(:jl_eqtable_get, Any, (Any, Any, Any), t.ht, key, default) -function pop!(t::ObjectIdDict, key::ANY, default::ANY) +function pop!(t::ObjectIdDict, @nospecialize(key), @nospecialize(default)) val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any), t.ht, key, default) # TODO: this can underestimate `ndel` val === default || (t.ndel += 1) return val end -function pop!(t::ObjectIdDict, key::ANY) +function pop!(t::ObjectIdDict, @nospecialize(key)) val = pop!(t, key, secret_table_token) val !== secret_table_token ? val : throw(KeyError(key)) end -function delete!(t::ObjectIdDict, key::ANY) +function delete!(t::ObjectIdDict, @nospecialize(key)) pop!(t, key, secret_table_token) t end diff --git a/base/base.jl b/base/base.jl index ee4bf7f3beed3c..11307307374765 100644 --- a/base/base.jl +++ b/base/base.jl @@ -54,9 +54,9 @@ mutable struct MethodError <: Exception f args world::UInt - MethodError(f::ANY, args::ANY, world::UInt) = new(f, args, world) + MethodError(@nospecialize(f), @nospecialize(args), world::UInt) = new(f, args, world) end -MethodError(f::ANY, args::ANY) = MethodError(f, args, typemax(UInt)) +MethodError(@nospecialize(f), @nospecialize(args)) = MethodError(f, args, typemax(UInt)) """ EOFError() @@ -122,7 +122,7 @@ ccall(:jl_get_system_hooks, Void, ()) ==(w::WeakRef, v) = isequal(w.value, v) ==(w, v::WeakRef) = isequal(w, v.value) -function finalizer(o::ANY, f::ANY) +function finalizer(@nospecialize(o), @nospecialize(f)) if isimmutable(o) error("objects of type ", typeof(o), " cannot be finalized") end @@ -138,8 +138,8 @@ function finalizer(o::T, f::Ptr{Void}) where T Core.getptls(), o, f) end -finalize(o::ANY) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), - Core.getptls(), o) +finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), + Core.getptls(), o) gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 diff --git a/base/boot.jl b/base/boot.jl index e5107db7faee80..8cfb42b0019152 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -183,7 +183,16 @@ else end function Typeof end -(f::typeof(Typeof))(x::ANY) = isa(x,Type) ? Type{x} : typeof(x) +ccall(:jl_toplevel_eval_in, Any, (Any, Any), + Core, quote + (f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x)) + end) + +macro nospecialize(x) + _expr(:meta, :nospecialize, x) +end + +Expr(@nospecialize args...) = _expr(args...) abstract type Exception end mutable struct ErrorException <: Exception @@ -191,8 +200,6 @@ mutable struct ErrorException <: Exception ErrorException(msg::AbstractString) = new(msg) end -Expr(args::ANY...) = _expr(args...) - macro _noinline_meta() Expr(:meta, :noinline) end @@ -201,8 +208,8 @@ struct BoundsError <: Exception a::Any i::Any BoundsError() = new() - BoundsError(a::ANY) = (@_noinline_meta; new(a)) - BoundsError(a::ANY, i) = (@_noinline_meta; new(a,i)) + BoundsError(@nospecialize(a)) = (@_noinline_meta; new(a)) + BoundsError(@nospecialize(a), i) = (@_noinline_meta; new(a,i)) end struct DivideError <: Exception end struct OverflowError <: Exception end @@ -218,8 +225,8 @@ struct InterruptException <: Exception end struct DomainError <: Exception val msg - DomainError(val::ANY) = (@_noinline_meta; new(val)) - DomainError(val::ANY, msg::ANY) = (@_noinline_meta; new(val, msg)) + DomainError(@nospecialize(val)) = (@_noinline_meta; new(val)) + DomainError(@nospecialize(val), @nospecialize(msg)) = (@_noinline_meta; new(val, msg)) end mutable struct TypeError <: Exception func::Symbol @@ -232,7 +239,7 @@ struct InexactError <: Exception T::Type val - InexactError(f::Symbol, T::ANY, val::ANY) = (@_noinline_meta; new(f, T, val)) + InexactError(f::Symbol, @nospecialize(T), @nospecialize(val)) = (@_noinline_meta; new(f, T, val)) end abstract type DirectIndexString <: AbstractString end @@ -244,16 +251,16 @@ getptls() = ccall(:jl_get_ptls_states, Ptr{Void}, ()) include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) -eval(e::ANY) = eval(Main, e) -eval(m::Module, e::ANY) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) +eval(@nospecialize(e)) = eval(Main, e) +eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) -kwfunc(f::ANY) = ccall(:jl_get_keyword_sorter, Any, (Any,), f) +kwfunc(@nospecialize(f)) = ccall(:jl_get_keyword_sorter, Any, (Any,), f) -kwftype(t::ANY) = typeof(ccall(:jl_get_kwsorter, Any, (Any,), t)) +kwftype(@nospecialize(t)) = typeof(ccall(:jl_get_kwsorter, Any, (Any,), t)) mutable struct Box contents::Any - Box(x::ANY) = new(x) + Box(@nospecialize(x)) = new(x) Box() = new() end @@ -262,18 +269,18 @@ end mutable struct WeakRef value WeakRef() = WeakRef(nothing) - WeakRef(v::ANY) = ccall(:jl_gc_new_weakref_th, Ref{WeakRef}, - (Ptr{Void}, Any), getptls(), v) + WeakRef(@nospecialize(v)) = ccall(:jl_gc_new_weakref_th, Ref{WeakRef}, + (Ptr{Void}, Any), getptls(), v) end TypeVar(n::Symbol) = ccall(:jl_new_typevar, Ref{TypeVar}, (Any, Any, Any), n, Union{}, Any) -TypeVar(n::Symbol, ub::ANY) = +TypeVar(n::Symbol, @nospecialize(ub)) = ccall(:jl_new_typevar, Ref{TypeVar}, (Any, Any, Any), n, Union{}, ub) -TypeVar(n::Symbol, lb::ANY, ub::ANY) = +TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = ccall(:jl_new_typevar, Ref{TypeVar}, (Any, Any, Any), n, lb, ub) -UnionAll(v::TypeVar, t::ANY) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) +UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) Void() = nothing @@ -288,26 +295,26 @@ VecElement(arg::T) where {T} = VecElement{T}(arg) # used by lowering of splicing unquote splicedexpr(hd::Symbol, args::Array{Any,1}) = (e=Expr(hd); e.args=args; e) -_new(typ::Symbol, argty::Symbol) = eval(:((::Type{$typ})(n::$argty) = $(Expr(:new, typ, :n)))) +_new(typ::Symbol, argty::Symbol) = eval(Core, :((::Type{$typ})(@nospecialize n::$argty) = $(Expr(:new, typ, :n)))) _new(:LabelNode, :Int) _new(:GotoNode, :Int) _new(:NewvarNode, :SlotNumber) -_new(:QuoteNode, :ANY) +_new(:QuoteNode, :Any) _new(:SSAValue, :Int) -eval(:((::Type{LineNumberNode})(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) -eval(:((::Type{LineNumberNode})(l::Int, f::ANY) = $(Expr(:new, :LineNumberNode, :l, :f)))) -eval(:((::Type{GlobalRef})(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) -eval(:((::Type{SlotNumber})(n::Int) = $(Expr(:new, :SlotNumber, :n)))) -eval(:((::Type{TypedSlot})(n::Int, t::ANY) = $(Expr(:new, :TypedSlot, :n, :t)))) +eval(Core, :((::Type{LineNumberNode})(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) +eval(Core, :((::Type{LineNumberNode})(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) +eval(Core, :((::Type{GlobalRef})(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) +eval(Core, :((::Type{SlotNumber})(n::Int) = $(Expr(:new, :SlotNumber, :n)))) +eval(Core, :((::Type{TypedSlot})(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool), name, std_imports) -Task(f::ANY) = ccall(:jl_new_task, Ref{Task}, (Any, Int), f, 0) +Task(@nospecialize(f)) = ccall(:jl_new_task, Ref{Task}, (Any, Int), f, 0) # simple convert for use by constructors of types in Core # note that there is no actual conversion defined here, # so the methods and ccall's in Core aren't permitted to use convert -convert(::Type{Any}, x::ANY) = x +convert(::Type{Any}, @nospecialize(x)) = x convert(::Type{T}, x::T) where {T} = x cconvert(::Type{T}, x) where {T} = convert(T, x) unsafe_convert(::Type{T}, x::T) where {T} = x @@ -382,16 +389,16 @@ function write(io::IO, x::String) return nb end -show(io::IO, x::ANY) = ccall(:jl_static_show, Void, (Ptr{Void}, Any), io_pointer(io), x) +show(io::IO, @nospecialize x) = ccall(:jl_static_show, Void, (Ptr{Void}, Any), io_pointer(io), x) print(io::IO, x::Char) = ccall(:jl_uv_putc, Void, (Ptr{Void}, Char), io_pointer(io), x) print(io::IO, x::String) = (write(io, x); nothing) -print(io::IO, x::ANY) = show(io, x) -print(io::IO, x::ANY, a::ANY...) = (print(io, x); print(io, a...)) +print(io::IO, @nospecialize x) = show(io, x) +print(io::IO, @nospecialize(x), @nospecialize a...) = (print(io, x); print(io, a...)) println(io::IO) = (write(io, 0x0a); nothing) # 0x0a = '\n' -println(io::IO, x::ANY...) = (print(io, x...); println(io)) +println(io::IO, @nospecialize x...) = (print(io, x...); println(io)) -show(a::ANY) = show(STDOUT, a) -print(a::ANY...) = print(STDOUT, a...) -println(a::ANY...) = println(STDOUT, a...) +show(@nospecialize a) = show(STDOUT, a) +print(@nospecialize a...) = print(STDOUT, a...) +println(@nospecialize a...) = println(STDOUT, a...) ccall(:jl_set_istopmod, Void, (Any, Bool), Core, true) diff --git a/base/client.jl b/base/client.jl index 1876c189d9354f..b705b76980357e 100644 --- a/base/client.jl +++ b/base/client.jl @@ -142,7 +142,7 @@ end display_error(er, bt) = display_error(STDERR, er, bt) display_error(er) = display_error(er, []) -function eval_user_input(ast::ANY, show_value) +function eval_user_input(@nospecialize(ast), show_value) errcount, lasterr, bt = 0, (), nothing while true try diff --git a/base/dates/adjusters.jl b/base/dates/adjusters.jl index 0958abe0934097..25d181e99369b3 100644 --- a/base/dates/adjusters.jl +++ b/base/dates/adjusters.jl @@ -186,7 +186,7 @@ lastdayofquarter(dt::DateTime) = DateTime(lastdayofquarter(Date(dt))) struct DateFunction f::Function # validate boolean, single-arg inner constructor - function DateFunction(f::ANY, dt::TimeType) + function DateFunction(@nospecialize(f), dt::TimeType) isa(f(dt), Bool) || throw(ArgumentError("Provided function must take a single TimeType argument and return true or false")) return new(f) end diff --git a/base/deepcopy.jl b/base/deepcopy.jl index d7d61ab813e51c..e4c0947d809359 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -51,7 +51,7 @@ function deepcopy_internal(x::String, stackdict::ObjectIdDict) return y end -function deepcopy_internal(x::ANY, stackdict::ObjectIdDict) +function deepcopy_internal(@nospecialize(x), stackdict::ObjectIdDict) T = typeof(x)::DataType nf = nfields(x) (isbits(T) || nf == 0) && return x @@ -78,7 +78,7 @@ function deepcopy_internal(x::Array, stackdict::ObjectIdDict) _deepcopy_array_t(x, eltype(x), stackdict) end -function _deepcopy_array_t(x::ANY, T, stackdict::ObjectIdDict) +function _deepcopy_array_t(@nospecialize(x), T, stackdict::ObjectIdDict) if isbits(T) return (stackdict[x]=copy(x)) end diff --git a/base/deprecated.jl b/base/deprecated.jl index 153d758d5b959f..c9752f3997652b 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1114,7 +1114,7 @@ end @deprecate(SharedArray(filename::AbstractString, ::Type{T}, dims::NTuple, offset; kwargs...) where {T}, SharedArray{T}(filename, dims, offset; kwargs...)) -@noinline function is_intrinsic_expr(x::ANY) +@noinline function is_intrinsic_expr(@nospecialize(x)) Base.depwarn("is_intrinsic_expr is deprecated. There are no intrinsic functions anymore.", :is_intrinsic_expr) return false end @@ -1372,11 +1372,11 @@ _current_module() = ccall(:jl_get_current_module, Ref{Module}, ()) depwarn("binding_module(symbol) is deprecated, use `binding_module(module, symbol)` instead.", :binding_module) return binding_module(_current_module(), s) end -@noinline function expand(x::ANY) +@noinline function expand(@nospecialize(x)) depwarn("expand(x) is deprecated, use `expand(module, x)` instead.", :expand) return expand(_current_module(), x) end -@noinline function macroexpand(x::ANY) +@noinline function macroexpand(@nospecialize(x)) depwarn("macroexpand(x) is deprecated, use `macroexpand(module, x)` instead.", :macroexpand) return macroexpand(_current_module(), x) end @@ -1576,6 +1576,9 @@ end # nfields(::Type) deprecation in builtins.c: update nfields tfunc in inference.jl when it is removed. # also replace `_nfields` with `nfields` in summarysize.c when this is removed. +# ::ANY is deprecated in src/method.c +# also remove all instances of `jl_ANY_flag` in src/ + # PR #22182 @deprecate is_apple Sys.isapple @deprecate is_bsd Sys.isbsd diff --git a/base/distributed/clusterserialize.jl b/base/distributed/clusterserialize.jl index 9fb256d1e261bb..2a42f35a7f572c 100644 --- a/base/distributed/clusterserialize.jl +++ b/base/distributed/clusterserialize.jl @@ -32,7 +32,7 @@ function lookup_object_number(s::ClusterSerializer, n::UInt64) return get(known_object_data, n, nothing) end -function remember_object(s::ClusterSerializer, o::ANY, n::UInt64) +function remember_object(s::ClusterSerializer, @nospecialize(o), n::UInt64) known_object_data[n] = o if isa(o, TypeName) && !haskey(object_numbers, o) # set up reverse mapping for serialize diff --git a/base/distributed/remotecall.jl b/base/distributed/remotecall.jl index d621f9ada06549..262828171283b4 100644 --- a/base/distributed/remotecall.jl +++ b/base/distributed/remotecall.jl @@ -484,7 +484,7 @@ Waits and fetches a value from `x` depending on the type of `x`: Does not remove the item fetched. """ -fetch(x::ANY) = x +fetch(@nospecialize x) = x isready(rv::RemoteValue, args...) = isready(rv.c, args...) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 42c0e22390e224..724a313f2a8dfe 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -146,7 +146,7 @@ mutable struct DocStr data :: Dict{Symbol, Any} end -function docstr(binding::Binding, typesig::ANY = Union{}) +function docstr(binding::Binding, @nospecialize typesig = Union{}) for m in modules dict = meta(m) if haskey(dict, binding) @@ -229,7 +229,7 @@ end Adds a new docstring `str` to the docsystem of `__module__` for `binding` and signature `sig`. """ -function doc!(__module__::Module, b::Binding, str::DocStr, sig::ANY = Union{}) +function doc!(__module__::Module, b::Binding, str::DocStr, @nospecialize sig = Union{}) initmeta(__module__) m = get!(meta(__module__), b, MultiDoc()) if haskey(m.docs, sig) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index c99b9f747cb40e..e675042c4373f6 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -689,14 +689,6 @@ The singleton instance of type `Void`, used by convention when there is no value """ nothing -""" - ANY - -Equivalent to `Any` for dispatch purposes, but signals the compiler to skip code -generation specialization for that field. -""" -ANY - """ Core.TypeofBottom diff --git a/base/docs/utils.jl b/base/docs/utils.jl index a4e4be3bc5603d..8621f4a08a327b 100644 --- a/base/docs/utils.jl +++ b/base/docs/utils.jl @@ -416,7 +416,7 @@ Strip all Markdown markup from x, leaving the result in plain text. Used internally by apropos to make docstrings containing more than one markdown element searchable. """ -stripmd(x::ANY) = string(x) # for random objects interpolated into the docstring +stripmd(@nospecialize x) = string(x) # for random objects interpolated into the docstring stripmd(x::AbstractString) = x # base case stripmd(x::Void) = " " stripmd(x::Vector) = string(map(stripmd, x)...) diff --git a/base/essentials.jl b/base/essentials.jl index 5dd052b025dd5c..38dab2076b033a 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -17,6 +17,42 @@ end macro _noinline_meta() Expr(:meta, :noinline) end + +""" + @nospecialize + +Applied to a function argument name, hints to the compiler that the method +should not be specialized for different types of that argument. +This is only a hint for avoiding excess code generation. +Can be applied to an argument within a formal argument list, or in the +function body. +When applied to an argument, the macro must wrap the entire argument +expression. +When used in a function body, the macro must occur in statement position and +before any code. + +```julia +function example_function(@nospecialize x) + ... +end + +function example_function(@nospecialize(x = 1), y) + ... +end + +function example_function(x, y, z) + @nospecialize x y + ... +end +``` +""" +macro nospecialize(var, vars...) + if isa(var, Expr) && var.head === :(=) + var.head = :kw + end + Expr(:meta, :nospecialize, var, vars...) +end + macro _pure_meta() Expr(:meta, :pure) end @@ -25,7 +61,7 @@ macro _propagate_inbounds_meta() Expr(:meta, :inline, :propagate_inbounds) end -convert(::Type{Any}, x::ANY) = x +convert(::Type{Any}, @nospecialize(x)) = x convert(::Type{T}, x::T) where {T} = x convert(::Type{Tuple{}}, ::Tuple{}) = () @@ -72,14 +108,14 @@ function tuple_type_cons(::Type{S}, ::Type{T}) where T<:Tuple where S Tuple{S, T.parameters...} end -function unwrap_unionall(a::ANY) +function unwrap_unionall(@nospecialize(a)) while isa(a,UnionAll) a = a.body end return a end -function rewrap_unionall(t::ANY, u::ANY) +function rewrap_unionall(@nospecialize(t), @nospecialize(u)) if !isa(u, UnionAll) return t end @@ -87,7 +123,7 @@ function rewrap_unionall(t::ANY, u::ANY) end # replace TypeVars in all enclosing UnionAlls with fresh TypeVars -function rename_unionall(u::ANY) +function rename_unionall(@nospecialize(u)) if !isa(u,UnionAll) return u end @@ -103,13 +139,13 @@ function rename_unionall(u::ANY) end const _va_typename = Vararg.body.body.name -function isvarargtype(t::ANY) +function isvarargtype(@nospecialize(t)) t = unwrap_unionall(t) isa(t, DataType) && (t::DataType).name === _va_typename end isvatuple(t::DataType) = (n = length(t.parameters); n > 0 && isvarargtype(t.parameters[n])) -function unwrapva(t::ANY) +function unwrapva(@nospecialize(t)) t2 = unwrap_unionall(t) isvarargtype(t2) ? t2.parameters[1] : t end @@ -171,9 +207,9 @@ function append_any(xs...) end # simple Array{Any} operations needed for bootstrap -setindex!(A::Array{Any}, x::ANY, i::Int) = Core.arrayset(A, x, i) +setindex!(A::Array{Any}, @nospecialize(x), i::Int) = Core.arrayset(A, x, i) -function precompile(f::ANY, args::Tuple) +function precompile(@nospecialize(f), args::Tuple) ccall(:jl_compile_hint, Int32, (Any,), Tuple{Core.Typeof(f), args...}) != 0 end @@ -182,13 +218,13 @@ function precompile(argt::Type) end """ - esc(e::ANY) + esc(e) Only valid in the context of an `Expr` returned from a macro. Prevents the macro hygiene pass from turning embedded variables into gensym variables. See the [Macros](@ref man-macros) section of the Metaprogramming chapter of the manual for more details and examples. """ -esc(e::ANY) = Expr(:escape, e) +esc(@nospecialize(e)) = Expr(:escape, e) macro boundscheck(blk) # hack: use this syntax since it avoids introducing line numbers @@ -339,7 +375,7 @@ end Val(x) = (@_pure_meta; Val{x}()) # used by interpolating quote and some other things in the front end -function vector_any(xs::ANY...) +function vector_any(@nospecialize xs...) n = length(xs) a = Vector{Any}(n) @inbounds for i = 1:n diff --git a/base/event.jl b/base/event.jl index fc7602218da038..6dfbe2a8fc1760 100644 --- a/base/event.jl +++ b/base/event.jl @@ -40,7 +40,7 @@ is raised as an exception in the woken tasks. Returns the count of tasks woken up. Returns 0 if no tasks are waiting on `condition`. """ -notify(c::Condition, arg::ANY=nothing; all=true, error=false) = notify(c, arg, all, error) +notify(c::Condition, @nospecialize(arg = nothing); all=true, error=false) = notify(c, arg, all, error) function notify(c::Condition, arg, all, error) cnt = 0 if all @@ -155,7 +155,7 @@ yield() = (enq_work(current_task()); wait()) A fast, unfair-scheduling version of `schedule(t, arg); yield()` which immediately yields to `t` before calling the scheduler. """ -function yield(t::Task, x::ANY = nothing) +function yield(t::Task, @nospecialize x = nothing) t.state == :runnable || error("schedule: Task not runnable") t.result = x enq_work(current_task()) @@ -170,7 +170,7 @@ called with no arguments. On subsequent switches, `arg` is returned from the tas call to `yieldto`. This is a low-level call that only switches tasks, not considering states or scheduling in any way. Its use is discouraged. """ -function yieldto(t::Task, x::ANY = nothing) +function yieldto(t::Task, @nospecialize x = nothing) t.result = x return try_yieldto(identity, Ref(t)) end @@ -194,7 +194,7 @@ function try_yieldto(undo, reftask::Ref{Task}) end # yield to a task, throwing an exception in it -function throwto(t::Task, exc::ANY) +function throwto(t::Task, @nospecialize exc) t.exception = exc return yieldto(t) end diff --git a/base/exports.jl b/base/exports.jl index ced228242fc29c..ac8c8dceca9c86 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1278,6 +1278,7 @@ export @simd, @inline, @noinline, + @nospecialize, @polly, @assert, diff --git a/base/expr.jl b/base/expr.jl index b005b441b3fe38..39998d41bc2fc4 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -39,7 +39,7 @@ copy(e::Expr) = (n = Expr(e.head); # copy parts of an AST that the compiler mutates copy_exprs(x::Expr) = copy(x) -copy_exprs(x::ANY) = x +copy_exprs(@nospecialize(x)) = x copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(a) for a in x] ==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args) @@ -52,7 +52,7 @@ Takes the expression `x` and returns an equivalent expression in lowered form for executing in module `m`. See also [`code_lowered`](@ref). """ -expand(m::Module, x::ANY) = ccall(:jl_expand, Any, (Any, Any), x, m) +expand(m::Module, @nospecialize(x)) = ccall(:jl_expand, Any, (Any, Any), x, m) """ macroexpand(m::Module, x; recursive=true) @@ -79,7 +79,7 @@ julia> macroexpand(M, :(@m2()), recursive=false) :(#= REPL[16]:6 =# M.@m1) ``` """ -function macroexpand(m::Module, x::ANY; recursive=true) +function macroexpand(m::Module, @nospecialize(x); recursive=true) if recursive ccall(:jl_macroexpand, Any, (Any, Any), x, m) else diff --git a/base/hashing.jl b/base/hashing.jl index efc87a46568cdc..331d6c214ea3e8 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -7,7 +7,7 @@ hash(w::WeakRef, h::UInt) = hash(w.value, h) ## hashing general objects ## -hash(x::ANY, h::UInt) = hash_uint(3h - object_id(x)) +hash(@nospecialize(x), h::UInt) = hash_uint(3h - object_id(x)) ## core data hashing functions ## diff --git a/base/inference.jl b/base/inference.jl index 3cdb8a47985947..6121b42a9f210a 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -68,15 +68,15 @@ const VarTable = Array{Any,1} mutable struct VarState typ undef::Bool - VarState(typ::ANY, undef::Bool) = new(typ, undef) + VarState(@nospecialize(typ), undef::Bool) = new(typ, undef) end # The type of a value might be constant struct Const val actual::Bool # if true, we obtained `val` by actually calling a @pure function - Const(v::ANY) = new(v, false) - Const(v::ANY, a::Bool) = new(v, a) + Const(@nospecialize(v)) = new(v, false) + Const(@nospecialize(v), a::Bool) = new(v, a) end # The type of a value might be Bool, @@ -89,9 +89,9 @@ mutable struct Conditional vtype elsetype function Conditional( - var::ANY, - vtype::ANY, - nottype::ANY) + @nospecialize(var), + @nospecialize(vtype), + @nospecialize(nottype)) return new(var, vtype, nottype) end end @@ -105,7 +105,7 @@ struct PartialTypeVar PartialTypeVar(tv::TypeVar, lb_certain::Bool, ub_certain::Bool) = new(tv, lb_certain, ub_certain) end -function rewrap(t::ANY, u::ANY) +function rewrap(@nospecialize(t), @nospecialize(u)) isa(t, Const) && return t isa(t, Conditional) && return t return rewrap_unionall(t, u) @@ -333,14 +333,14 @@ end @inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id # using a function to ensure we can infer this # avoid cycle due to over-specializing `any` when used by inference -function _any(f::ANY, a) +function _any(@nospecialize(f), a) for x in a f(x) && return true end return false end -function contains_is(itr, x::ANY) +function contains_is(itr, @nospecialize(x)) for y in itr if y === x return true @@ -354,7 +354,7 @@ anymap(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i=1:length(a) ] _topmod(sv::InferenceState) = _topmod(sv.mod) _topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module -function istopfunction(topmod, f::ANY, sym) +function istopfunction(topmod, @nospecialize(f), sym) if isdefined(Main, :Base) && isdefined(Main.Base, sym) && isconst(Main.Base, sym) && f === getfield(Main.Base, sym) return true elseif isdefined(topmod, sym) && isconst(topmod, sym) && f === getfield(topmod, sym) @@ -367,18 +367,18 @@ isknownlength(t::DataType) = !isvatuple(t) || (length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2],Int)) # t[n:end] -tupletype_tail(t::ANY, n) = Tuple{t.parameters[n:end]...} +tupletype_tail(@nospecialize(t), n) = Tuple{t.parameters[n:end]...} #### type-functions for builtins / intrinsics #### const _Type_name = Type.body.name -isType(t::ANY) = isa(t, DataType) && (t::DataType).name === _Type_name +isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _Type_name # true if Type is inlineable as constant (is a singleton) -isconstType(t::ANY) = isType(t) && (isleaftype(t.parameters[1]) || t.parameters[1] === Union{}) +isconstType(@nospecialize t) = isType(t) && (isleaftype(t.parameters[1]) || t.parameters[1] === Union{}) -iskindtype(t::ANY) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) +iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) const IInf = typemax(Int) # integer infinity const n_ifunc = reinterpret(Int32, arraylen) + 1 @@ -387,21 +387,22 @@ const t_ifunc_cost = Array{Int, 1}(n_ifunc) const t_ffunc_key = Array{Any, 1}(0) const t_ffunc_val = Array{Tuple{Int, Int, Any}, 1}(0) const t_ffunc_cost = Array{Int, 1}(0) -function add_tfunc(f::IntrinsicFunction, minarg::Int, maxarg::Int, tfunc::ANY, cost::Int) +function add_tfunc(f::IntrinsicFunction, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) idx = reinterpret(Int32, f) + 1 t_ifunc[idx] = (minarg, maxarg, tfunc) t_ifunc_cost[idx] = cost end -function add_tfunc(#=@nospecialize::Builtin=# f::Function, minarg::Int, maxarg::Int, tfunc::ANY, cost::Int) +# TODO: add @nospecialize on `f` and declare its type as `Builtin` when that's supported +function add_tfunc(f::Function, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) push!(t_ffunc_key, f) push!(t_ffunc_val, (minarg, maxarg, tfunc)) push!(t_ffunc_cost, cost) end -add_tfunc(throw, 1, 1, (x::ANY) -> Bottom, 0) +add_tfunc(throw, 1, 1, (@nospecialize(x)) -> Bottom, 0) # the inverse of typeof_tfunc -function instanceof_tfunc(t::ANY) +function instanceof_tfunc(@nospecialize(t)) # TODO improve if t === Bottom return t @@ -419,20 +420,20 @@ function instanceof_tfunc(t::ANY) end return Any end -bitcast_tfunc(t::ANY, x::ANY) = instanceof_tfunc(t) -math_tfunc(x::ANY) = widenconst(x) -math_tfunc(x::ANY, y::ANY) = widenconst(x) -math_tfunc(x::ANY, y::ANY, z::ANY) = widenconst(x) -fptoui_tfunc(t::ANY, x::ANY) = bitcast_tfunc(t, x) -fptosi_tfunc(t::ANY, x::ANY) = bitcast_tfunc(t, x) -function fptoui_tfunc(x::ANY) +bitcast_tfunc(@nospecialize(t), @nospecialize(x)) = instanceof_tfunc(t) +math_tfunc(@nospecialize(x)) = widenconst(x) +math_tfunc(@nospecialize(x), @nospecialize(y)) = widenconst(x) +math_tfunc(@nospecialize(x), @nospecialize(y), @nospecialize(z)) = widenconst(x) +fptoui_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) +fptosi_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) +function fptoui_tfunc(@nospecialize(x)) T = widenconst(x) T === Float64 && return UInt64 T === Float32 && return UInt32 T === Float16 && return UInt16 return Any end -function fptosi_tfunc(x::ANY) +function fptosi_tfunc(@nospecialize(x)) T = widenconst(x) T === Float64 && return Int64 T === Float32 && return Int32 @@ -501,7 +502,7 @@ add_tfunc(trunc_llvm, 1, 1, math_tfunc, 10) add_tfunc(rint_llvm, 1, 1, math_tfunc, 10) add_tfunc(sqrt_llvm, 1, 1, math_tfunc, 20) ## same-type comparisons ## -cmp_tfunc(x::ANY, y::ANY) = Bool +cmp_tfunc(@nospecialize(x), @nospecialize(y)) = Bool add_tfunc(eq_int, 2, 2, cmp_tfunc, 1) add_tfunc(ne_int, 2, 2, cmp_tfunc, 1) add_tfunc(slt_int, 2, 2, cmp_tfunc, 1) @@ -520,7 +521,7 @@ add_tfunc(lt_float_fast, 2, 2, cmp_tfunc, 1) add_tfunc(le_float_fast, 2, 2, cmp_tfunc, 1) ## checked arithmetic ## -chk_tfunc(x::ANY, y::ANY) = Tuple{widenconst(x), Bool} +chk_tfunc(@nospecialize(x), @nospecialize(y)) = Tuple{widenconst(x), Bool} add_tfunc(checked_sadd_int, 2, 2, chk_tfunc, 10) add_tfunc(checked_uadd_int, 2, 2, chk_tfunc, 10) add_tfunc(checked_ssub_int, 2, 2, chk_tfunc, 10) @@ -529,13 +530,13 @@ add_tfunc(checked_smul_int, 2, 2, chk_tfunc, 10) add_tfunc(checked_umul_int, 2, 2, chk_tfunc, 10) ## other, misc intrinsics ## add_tfunc(Core.Intrinsics.llvmcall, 3, IInf, - (fptr::ANY, rt::ANY, at::ANY, a...) -> instanceof_tfunc(rt), 10) -cglobal_tfunc(fptr::ANY) = Ptr{Void} -cglobal_tfunc(fptr::ANY, t::ANY) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) -cglobal_tfunc(fptr::ANY, t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) + (@nospecialize(fptr), @nospecialize(rt), @nospecialize(at), a...) -> instanceof_tfunc(rt), 10) +cglobal_tfunc(@nospecialize(fptr)) = Ptr{Void} +cglobal_tfunc(@nospecialize(fptr), @nospecialize(t)) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) +cglobal_tfunc(@nospecialize(fptr), t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) add_tfunc(Core.Intrinsics.cglobal, 1, 2, cglobal_tfunc, 5) add_tfunc(Core.Intrinsics.select_value, 3, 3, - function (cnd::ANY, x::ANY, y::ANY) + function (@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) if isa(cnd, Const) if cnd.val === true return x @@ -549,7 +550,7 @@ add_tfunc(Core.Intrinsics.select_value, 3, 3, return tmerge(x, y) end, 1) add_tfunc(===, 2, 2, - function (x::ANY, y::ANY) + function (@nospecialize(x), @nospecialize(y)) if isa(x, Const) && isa(y, Const) return Const(x.val === y.val) elseif typeintersect(widenconst(x), widenconst(y)) === Bottom @@ -608,7 +609,7 @@ function isdefined_tfunc(args...) end # TODO change IInf to 2 when deprecation is removed add_tfunc(isdefined, 1, IInf, isdefined_tfunc, 1) -_const_sizeof(x::ANY) = try +_const_sizeof(@nospecialize(x)) = try # Constant Vector does not have constant size isa(x, Vector) && return Int return Const(Core.sizeof(x)) @@ -616,16 +617,16 @@ catch return Int end add_tfunc(Core.sizeof, 1, 1, - function (x::ANY) + function (@nospecialize(x),) isa(x, Const) && return _const_sizeof(x.val) isa(x, Conditional) && return _const_sizeof(Bool) isconstType(x) && return _const_sizeof(x.parameters[1]) x !== DataType && isleaftype(x) && return _const_sizeof(x) return Int end, 0) -old_nfields(x::ANY) = length((isa(x,DataType) ? x : typeof(x)).types) +old_nfields(@nospecialize x) = length((isa(x,DataType) ? x : typeof(x)).types) add_tfunc(nfields, 1, 1, - function (x::ANY) + function (@nospecialize(x),) isa(x,Const) && return Const(old_nfields(x.val)) isa(x,Conditional) && return Const(old_nfields(Bool)) if isType(x) @@ -637,11 +638,11 @@ add_tfunc(nfields, 1, 1, return Int end, 0) add_tfunc(Core._expr, 1, IInf, (args...)->Expr, 100) -add_tfunc(applicable, 1, IInf, (f::ANY, args...)->Bool, 100) +add_tfunc(applicable, 1, IInf, (@nospecialize(f), args...)->Bool, 100) add_tfunc(Core.Intrinsics.arraylen, 1, 1, x->Int, 4) -add_tfunc(arraysize, 2, 2, (a::ANY, d::ANY)->Int, 4) +add_tfunc(arraysize, 2, 2, (@nospecialize(a), @nospecialize(d))->Int, 4) add_tfunc(pointerref, 3, 3, - function (a::ANY, i::ANY, align::ANY) + function (@nospecialize(a), @nospecialize(i), @nospecialize(align)) a = widenconst(a) if a <: Ptr if isa(a,DataType) && isa(a.parameters[1],Type) @@ -655,9 +656,9 @@ add_tfunc(pointerref, 3, 3, end return Any end, 4) -add_tfunc(pointerset, 4, 4, (a::ANY, v::ANY, i::ANY, align::ANY) -> a, 5) +add_tfunc(pointerset, 4, 4, (@nospecialize(a), @nospecialize(v), @nospecialize(i), @nospecialize(align)) -> a, 5) -function typeof_tfunc(t::ANY) +function typeof_tfunc(@nospecialize(t)) if isa(t, Const) return Const(typeof(t.val)) elseif isa(t, Conditional) @@ -691,7 +692,7 @@ function typeof_tfunc(t::ANY) end add_tfunc(typeof, 1, 1, typeof_tfunc, 0) add_tfunc(typeassert, 2, 2, - function (v::ANY, t::ANY) + function (@nospecialize(v), @nospecialize(t)) t = instanceof_tfunc(t) t === Any && return v if isa(v, Const) @@ -708,7 +709,7 @@ add_tfunc(typeassert, 2, 2, return typeintersect(v, t) end, 4) add_tfunc(isa, 2, 2, - function (v::ANY, t::ANY) + function (@nospecialize(v), @nospecialize(t)) t = instanceof_tfunc(t) if t !== Any && !has_free_typevars(t) if v ⊑ t @@ -721,7 +722,7 @@ add_tfunc(isa, 2, 2, return Bool end, 0) add_tfunc(issubtype, 2, 2, - function (a::ANY, b::ANY) + function (@nospecialize(a), @nospecialize(b)) if (isa(a,Const) || isType(a)) && (isa(b,Const) || isType(b)) a = instanceof_tfunc(a) b = instanceof_tfunc(b) @@ -732,7 +733,7 @@ add_tfunc(issubtype, 2, 2, return Bool end, 0) -function type_depth(t::ANY) +function type_depth(@nospecialize(t)) if t === Bottom return 0 elseif isa(t, Union) @@ -748,13 +749,13 @@ function type_depth(t::ANY) return 0 end -function limit_type_depth(t::ANY, d::Int) +function limit_type_depth(@nospecialize(t), d::Int) r = limit_type_depth(t, d, true, TypeVar[]) @assert !isa(t, Type) || t <: r return r end -function limit_type_depth(t::ANY, d::Int, cov::Bool, vars::Vector{TypeVar}=TypeVar[]) +function limit_type_depth(@nospecialize(t), d::Int, cov::Bool, vars::Vector{TypeVar}=TypeVar[]) if isa(t, Union) if d < 0 if cov @@ -820,7 +821,7 @@ end # limit the complexity of type `t` to be simpler than the comparison type `compare` # no new values may be introduced, so the parameter `source` encodes the set of all values already present -function limit_type_size(t::ANY, compare::ANY, source::ANY) +function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source)) source = svec(unwrap_unionall(compare), unwrap_unionall(source)) source[1] === source[2] && (source = svec(source[1])) type_more_complex(t, compare, source, TUPLE_COMPLEXITY_LIMIT_DEPTH) || return t @@ -834,7 +835,7 @@ end sym_isless(a::Symbol, b::Symbol) = ccall(:strcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}), a, b) < 0 -function type_more_complex(t::ANY, c::ANY, sources::SimpleVector, tupledepth::Int) +function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVector, tupledepth::Int) # detect cases where the comparison is trivial if t === c return false @@ -918,7 +919,7 @@ function type_more_complex(t::ANY, c::ANY, sources::SimpleVector, tupledepth::In return true end -function is_derived_type(t::ANY, c::ANY) # try to find `type` somewhere in `comparison` type +function is_derived_type(@nospecialize(t), @nospecialize(c)) # try to find `type` somewhere in `comparison` type t === c && return true if isa(c, TypeVar) # see if it is replacing a TypeVar upper bound with something simpler @@ -959,14 +960,14 @@ function is_derived_type(t::ANY, c::ANY) # try to find `type` somewhere in `comp return false end -function is_derived_type_from_any(t::ANY, sources::SimpleVector) +function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector) for s in sources is_derived_type(t, s) && return true end return false end -function _limit_type_size(t::ANY, c::ANY, sources::SimpleVector) # type vs. comparison which was derived from source +function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector) # type vs. comparison which was derived from source if t === c return t # quick egal test elseif t === Union{} @@ -1085,7 +1086,7 @@ function const_datatype_getfield_tfunc(sv, fld) end # returns (type, isexact) -function getfield_tfunc(s00::ANY, name) +function getfield_tfunc(@nospecialize(s00), name) if isa(s00, TypeVar) s00 = s00.ub end @@ -1205,9 +1206,9 @@ function getfield_tfunc(s00::ANY, name) # in the current type system return rewrap_unionall(limit_type_depth(R, MAX_TYPE_DEPTH), s00) end -add_tfunc(getfield, 2, 2, (s::ANY, name::ANY) -> getfield_tfunc(s, name), 1) -add_tfunc(setfield!, 3, 3, (o::ANY, f::ANY, v::ANY) -> v, 3) -function fieldtype_tfunc(s0::ANY, name::ANY) +add_tfunc(getfield, 2, 2, (@nospecialize(s), @nospecialize(name)) -> getfield_tfunc(s, name), 1) +add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) +function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 return Type end @@ -1268,7 +1269,7 @@ function fieldtype_tfunc(s0::ANY, name::ANY) end add_tfunc(fieldtype, 2, 2, fieldtype_tfunc, 0) -function valid_tparam(x::ANY) +function valid_tparam(@nospecialize(x)) if isa(x,Tuple) for t in x !valid_tparam(t) && return false @@ -1278,10 +1279,10 @@ function valid_tparam(x::ANY) return isa(x,Int) || isa(x,Symbol) || isa(x,Bool) || (!isa(x,Type) && isbits(x)) end -has_free_typevars(t::ANY) = ccall(:jl_has_free_typevars, Cint, (Any,), t)!=0 +has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t)!=0 # TODO: handle e.g. apply_type(T, R::Union{Type{Int32},Type{Float64}}) -function apply_type_tfunc(headtypetype::ANY, args::ANY...) +function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) if isa(headtypetype, Const) headtype = headtypetype.val elseif isType(headtypetype) && isleaftype(headtypetype.parameters[1]) @@ -1395,14 +1396,14 @@ function apply_type_tfunc(headtypetype::ANY, args::ANY...) end add_tfunc(apply_type, 1, IInf, apply_type_tfunc, 10) -@pure function type_typeof(v::ANY) +@pure function type_typeof(@nospecialize(v)) if isa(v, Type) return Type{v} end return typeof(v) end -function invoke_tfunc(f::ANY, types::ANY, argtype::ANY, sv::InferenceState) +function invoke_tfunc(@nospecialize(f), @nospecialize(types), @nospecialize(argtype), sv::InferenceState) if !isleaftype(Type{types}) return Any end @@ -1425,7 +1426,7 @@ function invoke_tfunc(f::ANY, types::ANY, argtype::ANY, sv::InferenceState) return rt end -function tuple_tfunc(argtype::ANY) +function tuple_tfunc(@nospecialize(argtype)) if isa(argtype, DataType) && argtype.name === Tuple.name p = Any[ isType(x) && !isa(x.parameters[1], TypeVar) ? typeof(x.parameters[1]) : x for x in argtype.parameters ] @@ -1437,7 +1438,7 @@ function tuple_tfunc(argtype::ANY) return argtype end -function builtin_tfunction(f::ANY, argtypes::Array{Any,1}, +function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, sv::Union{InferenceState,Void}, params::InferenceParams = sv.params) isva = !isempty(argtypes) && isvarargtype(argtypes[end]) if f === tuple @@ -1524,9 +1525,9 @@ function builtin_tfunction(f::ANY, argtypes::Array{Any,1}, return tf[3](argtypes...) end -limit_tuple_depth(params::InferenceParams, t::ANY) = limit_tuple_depth_(params,t,0) +limit_tuple_depth(params::InferenceParams, @nospecialize(t)) = limit_tuple_depth_(params,t,0) -function limit_tuple_depth_(params::InferenceParams, t::ANY, d::Int) +function limit_tuple_depth_(params::InferenceParams, @nospecialize(t), d::Int) if isa(t,Union) # also limit within Union types. # may have to recur into other stuff in the future too. @@ -1552,9 +1553,9 @@ function limit_tuple_depth_(params::InferenceParams, t::ANY, d::Int) Tuple{p...} end -limit_tuple_type = (t::ANY, params::InferenceParams) -> limit_tuple_type_n(t, params.MAX_TUPLETYPE_LEN) +limit_tuple_type = (@nospecialize(t), params::InferenceParams) -> limit_tuple_type_n(t, params.MAX_TUPLETYPE_LEN) -function limit_tuple_type_n(t::ANY, lim::Int) +function limit_tuple_type_n(@nospecialize(t), lim::Int) if isa(t,UnionAll) return UnionAll(t.var, limit_tuple_type_n(t.body, lim)) end @@ -1569,7 +1570,7 @@ end # return an upper-bound on type `a` with type `b` removed # such that `return <: a` && `Union{return, b} == Union{a, b}` -function typesubtract(a::ANY, b::ANY) +function typesubtract(@nospecialize(a), @nospecialize(b)) if a <: b return Bottom end @@ -1585,12 +1586,12 @@ end # take a Tuple where one or more parameters are Unions # and return an array such that those Unions are removed # and `Union{return...} == ty` -function switchtupleunion(ty::ANY) +function switchtupleunion(@nospecialize(ty)) tparams = (unwrap_unionall(ty)::DataType).parameters return _switchtupleunion(Any[tparams...], length(tparams), [], ty) end -function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, origt::ANY) +function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt)) if i == 0 tpl = rewrap_unionall(Tuple{t...}, origt) push!(tunion, tpl) @@ -1609,7 +1610,7 @@ function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, origt::A return tunion end -function abstract_call_gf_by_type(f::ANY, atype::ANY, sv::InferenceState) +function abstract_call_gf_by_type(@nospecialize(f), @nospecialize(atype), sv::InferenceState) # don't consider more than N methods. this trades off between # compiler performance and generated code performance. # typically, considering many methods means spending lots of time @@ -1692,7 +1693,7 @@ function abstract_call_gf_by_type(f::ANY, atype::ANY, sv::InferenceState) return rettype end -function abstract_call_method(method::Method, f::ANY, sig::ANY, sparams::SimpleVector, sv::InferenceState) +function abstract_call_method(method::Method, @nospecialize(f), @nospecialize(sig), sparams::SimpleVector, sv::InferenceState) sigtuple = unwrap_unionall(sig)::DataType tm = _topmod(sv) @@ -1798,7 +1799,7 @@ function abstract_call_method(method::Method, f::ANY, sig::ANY, sparams::SimpleV end # determine whether `ex` abstractly evals to constant `c` -function abstract_evals_to_constant(ex::ANY, c::ANY, vtypes::VarTable, sv::InferenceState) +function abstract_evals_to_constant(@nospecialize(ex), @nospecialize(c), vtypes::VarTable, sv::InferenceState) av = abstract_eval(ex, vtypes, sv) return isa(av,Const) && av.val === c end @@ -1808,7 +1809,7 @@ end # refine its type to an array of element types. # Union of Tuples of the same length is converted to Tuple of Unions. # returns an array of types -function precise_container_type(arg::ANY, typ::ANY, vtypes::VarTable, sv::InferenceState) +function precise_container_type(@nospecialize(arg), @nospecialize(typ), vtypes::VarTable, sv::InferenceState) if isa(typ, Const) val = typ.val if isa(val, SimpleVector) || isa(val, Tuple) @@ -1861,7 +1862,7 @@ function precise_container_type(arg::ANY, typ::ANY, vtypes::VarTable, sv::Infere end # simulate iteration protocol on container type up to fixpoint -function abstract_iteration(itertype::ANY, vtypes::VarTable, sv::InferenceState) +function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::InferenceState) tm = _topmod(sv) if !isdefined(tm, :start) || !isdefined(tm, :next) || !isconst(tm, :start) || !isconst(tm, :next) return Vararg{Any} @@ -1886,12 +1887,12 @@ function abstract_iteration(itertype::ANY, vtypes::VarTable, sv::InferenceState) return Vararg{valtype} end -function tuple_tail_elem(init::ANY, ct) +function tuple_tail_elem(@nospecialize(init), ct) return Vararg{widenconst(foldl((a, b) -> tmerge(a, unwrapva(b)), init, ct))} end # do apply(af, fargs...), where af is a function value -function abstract_apply(aft::ANY, fargs::Vector{Any}, aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) +function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) if !isa(aft, Const) && !isconstType(aft) if !(isleaftype(aft) || aft <: Type) || (aft <: Builtin) || (aft <: IntrinsicFunction) return Any @@ -1943,7 +1944,7 @@ end # TODO: this function is a very buggy and poor model of the return_type function # since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, # while this assumes that it is a precisely accurate and exact model of both -function return_type_tfunc(argtypes::ANY, vtypes::VarTable, sv::InferenceState) +function return_type_tfunc(@nospecialize(argtypes), vtypes::VarTable, sv::InferenceState) if length(argtypes) == 3 tt = argtypes[3] if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) @@ -1981,7 +1982,7 @@ function return_type_tfunc(argtypes::ANY, vtypes::VarTable, sv::InferenceState) return NF end -function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, sv::InferenceState) +function pure_eval_call(@nospecialize(f), @nospecialize(argtypes), @nospecialize(atype), sv::InferenceState) for i = 2:length(argtypes) a = argtypes[i] if !(isa(a,Const) || isconstType(a)) @@ -2038,9 +2039,9 @@ _typename(union::UnionAll) = _typename(union.body) # N.B.: typename maps type equivalence classes to a single value typename_static(t::Const) = _typename(t.val) -typename_static(t::ANY) = isType(t) ? _typename(t.parameters[1]) : Any +typename_static(@nospecialize(t)) = isType(t) ? _typename(t.parameters[1]) : Any -function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) +function abstract_call(@nospecialize(f), fargs::Union{Tuple{},Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) if f === _apply length(fargs) > 1 || return Any return abstract_apply(argtypes[2], fargs[3:end], argtypes[3:end], vtypes, sv) @@ -2291,7 +2292,7 @@ end const _Ref_name = Ref.body.name -function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) +function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) if isa(e, QuoteNode) return abstract_eval_constant((e::QuoteNode).value) elseif isa(e, SSAValue) @@ -2450,7 +2451,7 @@ mutable struct StateUpdate state::VarTable end -function abstract_interpret(e::ANY, vtypes::VarTable, sv::InferenceState) +function abstract_interpret(@nospecialize(e), vtypes::VarTable, sv::InferenceState) !isa(e, Expr) && return vtypes # handle assignment if e.head === :(=) @@ -2476,7 +2477,7 @@ function abstract_interpret(e::ANY, vtypes::VarTable, sv::InferenceState) return vtypes end -function type_too_complex(t::ANY, d::Int) +function type_too_complex(@nospecialize(t), d::Int) if d < 0 return true elseif isa(t, Union) @@ -2511,7 +2512,7 @@ function issubconditional(a::Conditional, b::Conditional) return false end -function ⊑(a::ANY, b::ANY) +function ⊑(@nospecialize(a), @nospecialize(b)) (a === NF || b === Any) && return true (a === Any || b === NF) && return false a === Union{} && return true @@ -2551,7 +2552,7 @@ function widenconst(c::Const) end end widenconst(c::PartialTypeVar) = TypeVar -widenconst(t::ANY) = t +widenconst(@nospecialize(t)) = t issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) @@ -2562,7 +2563,7 @@ is_meta_expr_head(head::Symbol) = head === :line || head === :simdloop) is_meta_expr(ex::Expr) = is_meta_expr_head(ex.head) -function tmerge(typea::ANY, typeb::ANY) +function tmerge(@nospecialize(typea), @nospecialize(typeb)) typea ⊑ typeb && return typeb typeb ⊑ typea && return typea if isa(typea, Conditional) && isa(typeb, Conditional) @@ -2609,8 +2610,8 @@ function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) return VarState(tmerge(sa.typ, sb.typ), sa.undef | sb.undef) end -@inline tchanged(n::ANY, o::ANY) = o === NF || (n !== NF && !(n ⊑ o)) -@inline schanged(n::ANY, o::ANY) = (n !== o) && (o === NF || (n !== NF && !issubstate(n, o))) +@inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NF || (n !== NF && !(n ⊑ o)) +@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NF || (n !== NF && !issubstate(n, o))) function stupdate!(state::Tuple{}, changes::StateUpdate) newst = copy(changes.state) @@ -2724,7 +2725,7 @@ function find_ssavalue_defs(body::Vector{Any}, nvals::Int) return defs end -function newvar!(sv::InferenceState, typ::ANY) +function newvar!(sv::InferenceState, @nospecialize(typ)) id = length(sv.src.ssavaluetypes) push!(sv.src.ssavaluetypes, typ) return SSAValue(id) @@ -2755,7 +2756,7 @@ function add_backedge!(li::MethodInstance, caller::InferenceState) end # temporarily accumulate our no method errors to later add as backedges in the callee method table -function add_mt_backedge(mt::MethodTable, typ::ANY, caller::InferenceState) +function add_mt_backedge(mt::MethodTable, @nospecialize(typ), caller::InferenceState) isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs if caller.stmt_edges[caller.currpc] === () caller.stmt_edges[caller.currpc] = [] @@ -2788,7 +2789,7 @@ function finalize_backedges(frame::InferenceState) end end -function code_for_method(method::Method, atypes::ANY, sparams::SimpleVector, world::UInt, preexisting::Bool=false) +function code_for_method(method::Method, @nospecialize(atypes), sparams::SimpleVector, world::UInt, preexisting::Bool=false) if world < min_world(method) return nothing end @@ -2893,7 +2894,7 @@ function typeinf_frame(linfo::MethodInstance, end # compute (and cache) an inferred AST and return the current best estimate of the result type -function typeinf_edge(method::Method, atypes::ANY, sparams::SimpleVector, caller::InferenceState) +function typeinf_edge(method::Method, @nospecialize(atypes), sparams::SimpleVector, caller::InferenceState) code = code_for_method(method, atypes, sparams, caller.params.world) code === nothing && return Any, nothing code = code::MethodInstance @@ -2932,7 +2933,7 @@ end #### entry points for inferring a MethodInstance given a type signature #### # compute an inferred AST and return type -function typeinf_code(method::Method, atypes::ANY, sparams::SimpleVector, +function typeinf_code(method::Method, @nospecialize(atypes), sparams::SimpleVector, optimize::Bool, cached::Bool, params::InferenceParams) code = code_for_method(method, atypes, sparams, params.world) code === nothing && return (nothing, nothing, Any) @@ -2980,7 +2981,7 @@ function typeinf_code(linfo::MethodInstance, optimize::Bool, cached::Bool, end # compute (and cache) an inferred AST and return the inferred return type -function typeinf_type(method::Method, atypes::ANY, sparams::SimpleVector, +function typeinf_type(method::Method, @nospecialize(atypes), sparams::SimpleVector, cached::Bool, params::InferenceParams) code = code_for_method(method, atypes, sparams, params.world) code === nothing && return nothing @@ -3235,7 +3236,7 @@ function typeinf(frame::InferenceState) end -function record_ssa_assign(ssa_id::Int, new::ANY, frame::InferenceState) +function record_ssa_assign(ssa_id::Int, @nospecialize(new), frame::InferenceState) old = frame.src.ssavaluetypes[ssa_id] if old === NF || !(new ⊑ old) frame.src.ssavaluetypes[ssa_id] = tmerge(old, new) @@ -3624,7 +3625,7 @@ end # widen all slots to their optimal storage layout # we also need to preserve the type for any untyped load of a DataType # since codegen optimizations of functions like `is` will depend on knowing it -function widen_slot_type(ty::ANY, untypedload::Bool) +function widen_slot_type(@nospecialize(ty), untypedload::Bool) ty = widenconst(ty) if isa(ty, DataType) if untypedload || isbits(ty) || isdefined(ty, :instance) @@ -3647,7 +3648,7 @@ end # replace slots 1:na with argexprs, static params with spvals, and increment # other slots by offset. -function substitute!(e::ANY, na::Int, argexprs::Vector{Any}, spsig::ANY, spvals::Vector{Any}, offset::Int) +function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecialize(spsig), spvals::Vector{Any}, offset::Int) if isa(e, Slot) id = slot_id(e) if 1 <= id <= na @@ -3696,7 +3697,7 @@ function substitute!(e::ANY, na::Int, argexprs::Vector{Any}, spsig::ANY, spvals: end # count occurrences up to n+1 -function occurs_more(e::ANY, pred, n) +function occurs_more(@nospecialize(e), pred, n) if isa(e,Expr) e = e::Expr head = e.head @@ -3716,7 +3717,7 @@ function occurs_more(e::ANY, pred, n) return 0 end -function exprtype(x::ANY, src::CodeInfo, mod::Module) +function exprtype(@nospecialize(x), src::CodeInfo, mod::Module) if isa(x, Expr) return (x::Expr).typ elseif isa(x, SlotNumber) @@ -3754,14 +3755,14 @@ function is_pure_intrinsic(f::IntrinsicFunction) f === Intrinsics.cglobal) # cglobal throws an error for symbol-not-found end -function is_pure_builtin(f::ANY) +function is_pure_builtin(@nospecialize(f)) return (contains_is(_pure_builtins, f) || contains_is(_pure_builtins_volatile, f) || (isa(f,IntrinsicFunction) && is_pure_intrinsic(f)) || f === return_type) end -function statement_effect_free(e::ANY, src::CodeInfo, mod::Module) +function statement_effect_free(@nospecialize(e), src::CodeInfo, mod::Module) if isa(e, Expr) if e.head === :(=) return !isa(e.args[1], GlobalRef) && effect_free(e.args[2], src, mod, false) @@ -3777,7 +3778,7 @@ end # detect some important side-effect-free calls (allow_volatile=true) # and some affect-free calls (allow_volatile=false) -- affect_free means the call # cannot be affected by previous calls, except assignment nodes -function effect_free(e::ANY, src::CodeInfo, mod::Module, allow_volatile::Bool) +function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatile::Bool) if isa(e, GlobalRef) return (isdefined(e.mod, e.name) && (allow_volatile || isconst(e.mod, e.name))) elseif isa(e, Symbol) @@ -3865,7 +3866,7 @@ struct InvokeData texpr end -function inline_as_constant(val::ANY, argexprs, sv::InferenceState, invoke_data::ANY) +function inline_as_constant(@nospecialize(val), argexprs, sv::InferenceState, @nospecialize(invoke_data)) if invoke_data === nothing invoke_fexpr = nothing invoke_texpr = nothing @@ -3891,7 +3892,7 @@ function inline_as_constant(val::ANY, argexprs, sv::InferenceState, invoke_data: return (val, stmts) end -function is_self_quoting(x::ANY) +function is_self_quoting(@nospecialize(x)) return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) end @@ -3905,7 +3906,7 @@ function countunionsplit(atypes) return nu end -function get_spec_lambda(atypes::ANY, sv, invoke_data::ANY) +function get_spec_lambda(@nospecialize(atypes), sv, @nospecialize(invoke_data)) if invoke_data === nothing return ccall(:jl_get_spec_lambda, Any, (Any, UInt), atypes, sv.params.world) else @@ -3935,8 +3936,8 @@ function linearize_args!(args::Vector{Any}, atypes::Vector{Any}, stmts::Vector{A return newargs end -function invoke_NF(argexprs, etype::ANY, atypes::Vector{Any}, sv::InferenceState, - atype_unlimited::ANY, invoke_data::ANY) +function invoke_NF(argexprs, @nospecialize(etype), atypes::Vector{Any}, sv::InferenceState, + @nospecialize(atype_unlimited), @nospecialize(invoke_data)) # converts a :call to :invoke nu = countunionsplit(atypes) nu > sv.params.MAX_UNION_SPLITTING && return NF @@ -4065,7 +4066,7 @@ end # `ft` is the type of the function. `f` is the exact function if known, or else `nothing`. # `pending_stmts` is an array of statements from functions inlined so far, so # we can estimate the total size of the enclosing function after inlining. -function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::InferenceState, +function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector{Any}, sv::InferenceState, pending_stmts) argexprs = e.args @@ -4519,7 +4520,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end end - inlining_ignore = function (stmt::ANY) + inlining_ignore = function (@nospecialize(stmt),) isa(stmt, Expr) && return is_meta_expr(stmt::Expr) isa(stmt, LineNumberNode) && return true stmt === nothing && return true @@ -4685,14 +4686,14 @@ function inline_worthy(body::Expr, src::CodeInfo, mod::Module, params::Inference bodycost <= cost_threshold end -function inline_worthy(body::ANY, src::CodeInfo, mod::Module, params::InferenceParams, +function inline_worthy(@nospecialize(body), src::CodeInfo, mod::Module, params::InferenceParams, cost_threshold::Integer=params.inline_cost_threshold) newbody = exprtype(body, src, mod) !isa(newbody, Expr) && return true inline_worthy(newbody, src, mod, params, cost_threshold) end -ssavalue_increment(body::ANY, incr) = body +ssavalue_increment(@nospecialize(body), incr) = body ssavalue_increment(body::SSAValue, incr) = SSAValue(body.id + incr) function ssavalue_increment(body::Expr, incr) if is_meta_expr(body) @@ -4988,7 +4989,7 @@ end const compiler_temp_sym = Symbol("#temp#") -function add_slot!(src::CodeInfo, typ::ANY, is_sa::Bool, name::Symbol=compiler_temp_sym) +function add_slot!(src::CodeInfo, @nospecialize(typ), is_sa::Bool, name::Symbol=compiler_temp_sym) @assert !isa(typ, Const) && !isa(typ, Conditional) id = length(src.slotnames) + 1 push!(src.slotnames, name) @@ -4997,7 +4998,7 @@ function add_slot!(src::CodeInfo, typ::ANY, is_sa::Bool, name::Symbol=compiler_t return SlotNumber(id) end -function is_known_call(e::Expr, func::ANY, src::CodeInfo, mod::Module) +function is_known_call(e::Expr, @nospecialize(func), src::CodeInfo, mod::Module) if e.head !== :call return false end @@ -5005,7 +5006,7 @@ function is_known_call(e::Expr, func::ANY, src::CodeInfo, mod::Module) return isa(f, Const) && f.val === func end -function is_known_call_p(e::Expr, pred::ANY, src::CodeInfo, mod::Module) +function is_known_call_p(e::Expr, @nospecialize(pred), src::CodeInfo, mod::Module) if e.head !== :call return false end @@ -5013,7 +5014,7 @@ function is_known_call_p(e::Expr, pred::ANY, src::CodeInfo, mod::Module) return (isa(f, Const) && pred(f.val)) || (isType(f) && pred(f.parameters[1])) end -function record_used(e::ANY, T::ANY, used::Vector{Bool}) +function record_used(@nospecialize(e), @nospecialize(T), used::Vector{Bool}) if isa(e,T) used[e.id+1] = true elseif isa(e,Expr) @@ -5058,7 +5059,7 @@ function replace_vars!(src::CodeInfo, r::ObjectIdDict) return src end -function _replace_vars!(e::ANY, r::ObjectIdDict) +function _replace_vars!(@nospecialize(e), r::ObjectIdDict) if isa(e, SSAValue) || isa(e, Slot) v = normvar(e) if haskey(r, v) @@ -5086,11 +5087,11 @@ normslot(s::SlotNumber) = s normslot(s::TypedSlot) = SlotNumber(slot_id(s)) normvar(s::Slot) = normslot(s) normvar(s::SSAValue) = s -normvar(s::ANY) = s +normvar(@nospecialize(s)) = s # given a single-assigned var and its initializer `init`, return what we can # replace `var` with, or `var` itself if we shouldn't replace it -function get_replacement(table, var::Union{SlotNumber, SSAValue}, init::ANY, nargs, slottypes, ssavaluetypes) +function get_replacement(table, var::Union{SlotNumber, SSAValue}, @nospecialize(init), nargs, slottypes, ssavaluetypes) #if isa(init, QuoteNode) # this can cause slight code size increases # return init if (isa(init, Expr) && init.head === :static_parameter) || isa(init, corenumtype) || @@ -5187,10 +5188,10 @@ end symequal(x::SSAValue, y::SSAValue) = x.id === y.id symequal(x::Slot , y::Slot) = x.id === y.id -symequal(x::ANY , y::ANY) = x === y +symequal(@nospecialize(x) , @nospecialize(y)) = x === y -function occurs_outside_getfield(e::ANY, sym::ANY, - sv::InferenceState, field_count::Int, field_names::ANY) +function occurs_outside_getfield(@nospecialize(e), @nospecialize(sym), + sv::InferenceState, field_count::Int, @nospecialize(field_names)) if e === sym || (isa(e, Slot) && isa(sym, Slot) && slot_id(e) == slot_id(sym)) return true end @@ -5234,7 +5235,7 @@ end function void_use_elim_pass!(sv::InferenceState) # Remove top level SSAValue and slots that is `!usedUndef`. # Also remove some `nothing` while we are at it.... - not_void_use = function (ex::ANY) + not_void_use = function (@nospecialize(ex),) if isa(ex, SSAValue) # Explicitly listed here for clarity return false @@ -5588,12 +5589,12 @@ function _getfield_elim_pass!(e::Expr, sv::InferenceState) return e end -_getfield_elim_pass!(e::ANY, sv) = e +_getfield_elim_pass!(@nospecialize(e), sv) = e # check if e is a successful allocation of an struct # if it is, returns (n,f) such that it is always valid to call # getfield(..., 1 <= x <= n) or getfield(..., x in f) on the result -function is_allocation(e::ANY, sv::InferenceState) +function is_allocation(@nospecialize(e), sv::InferenceState) isa(e, Expr) || return false if is_known_call(e, tuple, sv.src, sv.mod) return (length(e.args)-1,()) @@ -5871,7 +5872,7 @@ function reindex_labels!(sv::InferenceState) end end -function return_type(f::ANY, t::ANY) +function return_type(@nospecialize(f), @nospecialize(t)) params = InferenceParams(ccall(:jl_get_tls_world_age, UInt, ())) rt = Union{} if isa(f, Builtin) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index f08e1d65456a4e..94e37844e11fb5 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -84,8 +84,8 @@ Edit the definition of a function, optionally specifying a tuple of types to indicate which method to edit. The editor can be changed by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. """ -edit(f) = edit(functionloc(f)...) -edit(f, t::ANY) = edit(functionloc(f,t)...) +edit(f) = edit(functionloc(f)...) +edit(f, @nospecialize t) = edit(functionloc(f,t)...) edit(file, line::Integer) = error("could not find source file for function") # terminal pager @@ -117,8 +117,8 @@ less(file::AbstractString) = less(file, 1) Show the definition of a function using the default pager, optionally specifying a tuple of types to indicate which method to see. """ -less(f) = less(functionloc(f)...) -less(f, t::ANY) = less(functionloc(f,t)...) +less(f) = less(functionloc(f)...) +less(f, @nospecialize t) = less(functionloc(f,t)...) less(file, line::Integer) = error("could not find source file for function") # clipboard copy and paste @@ -343,7 +343,7 @@ This serves as a warning of potential type instability. Not all non-leaf types a problematic for performance, so the results need to be used judiciously. See [`@code_warntype`](@ref man-code-warntype) for more information. """ -function code_warntype(io::IO, f, t::ANY) +function code_warntype(io::IO, f, @nospecialize(t)) emph_io = IOContext(io, :TYPEEMPHASIZE => true) for (src, rettype) in code_typed(f, t) println(emph_io, "Variables:") @@ -367,7 +367,7 @@ function code_warntype(io::IO, f, t::ANY) end nothing end -code_warntype(f, t::ANY) = code_warntype(STDOUT, f, t) +code_warntype(f, @nospecialize(t)) = code_warntype(STDOUT, f, t) typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} @@ -525,7 +525,7 @@ Evaluates the arguments to the function or macro call, determines their types, a """ :@code_native -function type_close_enough(x::ANY, t::ANY) +function type_close_enough(@nospecialize(x), @nospecialize(t)) x == t && return true return (isa(x,DataType) && isa(t,DataType) && x.name === t.name && !isleaftype(t) && x <: t) || @@ -551,7 +551,7 @@ function methodswith(t::Type, f::Function, showparents::Bool=false, meths = Meth (type_close_enough(x, t) || (showparents ? (t <: x && (!isa(x,TypeVar) || x.ub != Any)) : (isa(x,TypeVar) && x.ub != Any && t == x.ub)) && - x != Any && x != ANY) + x != Any) end end, unwrap_unionall(d.sig).parameters) diff --git a/base/libuv.jl b/base/libuv.jl index 0a934484642f2f..1380b6e933e337 100644 --- a/base/libuv.jl +++ b/base/libuv.jl @@ -39,7 +39,7 @@ macro handle_as(hand, typ) end end -associate_julia_struct(handle::Ptr{Void}, jlobj::ANY) = +associate_julia_struct(handle::Ptr{Void}, @nospecialize(jlobj)) = ccall(:jl_uv_associate_julia_struct, Void, (Ptr{Void}, Any), handle, jlobj) disassociate_julia_struct(uv) = disassociate_julia_struct(uv.handle) disassociate_julia_struct(handle::Ptr{Void}) = diff --git a/base/operators.jl b/base/operators.jl index 7cafe19954a359..b5d5303420bd78 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -25,7 +25,7 @@ const (<:) = issubtype Supertype operator, equivalent to `issubtype(T2, T1)`. """ -const (>:)(a::ANY, b::ANY) = issubtype(b, a) +const (>:)(@nospecialize(a), @nospecialize(b)) = issubtype(b, a) """ supertype(T::DataType) diff --git a/base/pointer.jl b/base/pointer.jl index 9ef25ebcd0936f..fee9420a399502 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -92,7 +92,7 @@ The `unsafe` prefix on this function indicates that no validation is performed o pointer `p` to ensure that it is valid. Incorrect usage may corrupt or segfault your program, in the same manner as C. """ -unsafe_store!(p::Ptr{Any}, x::ANY, i::Integer=1) = pointerset(p, x, Int(i), 1) +unsafe_store!(p::Ptr{Any}, @nospecialize(x), i::Integer=1) = pointerset(p, x, Int(i), 1) unsafe_store!(p::Ptr{T}, x, i::Integer=1) where {T} = pointerset(p, convert(T,x), Int(i), 1) # convert a raw Ptr to an object reference, and vice-versa @@ -112,8 +112,8 @@ Get the memory address of a Julia object as a `Ptr`. The existence of the result will not protect the object from garbage collection, so you must ensure that the object remains referenced for the whole time that the `Ptr` will be used. """ -pointer_from_objref(x::ANY) = ccall(:jl_value_ptr, Ptr{Void}, (Any,), x) -data_pointer_from_objref(x::ANY) = pointer_from_objref(x)::Ptr{Void} +pointer_from_objref(@nospecialize(x)) = ccall(:jl_value_ptr, Ptr{Void}, (Any,), x) +data_pointer_from_objref(@nospecialize(x)) = pointer_from_objref(x)::Ptr{Void} eltype(::Type{Ptr{T}}) where {T} = T diff --git a/base/promotion.jl b/base/promotion.jl index b2d163ecd1614e..10c8b2a34170ec 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -3,9 +3,9 @@ ## type join (closest common ancestor, or least upper bound) ## typejoin() = (@_pure_meta; Bottom) -typejoin(t::ANY) = (@_pure_meta; t) -typejoin(t::ANY, ts...) = (@_pure_meta; typejoin(t, typejoin(ts...))) -function typejoin(a::ANY, b::ANY) +typejoin(@nospecialize(t)) = (@_pure_meta; t) +typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...))) +function typejoin(@nospecialize(a), @nospecialize(b)) @_pure_meta if a <: b return b @@ -312,7 +312,7 @@ _default_type(T::Type) = (@_inline_meta; T) if isdefined(Core, :Inference) const _return_type = Core.Inference.return_type else - _return_type(f::ANY, t::ANY) = Any + _return_type(@nospecialize(f), @nospecialize(t)) = Any end promote_op(::Any...) = (@_inline_meta; Any) diff --git a/base/range.jl b/base/range.jl index b333cf2492a5ae..7af3ec4520298c 100644 --- a/base/range.jl +++ b/base/range.jl @@ -890,7 +890,7 @@ in(x::Char, r::Range{Char}) = # Addition/subtraction of ranges -function _define_range_op(f::ANY) +function _define_range_op(@nospecialize f) @eval begin function $f(r1::OrdinalRange, r2::OrdinalRange) r1l = length(r1) diff --git a/base/reflection.jl b/base/reflection.jl index bbf8b130d6a4ec..3a7cff9899bf5a 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -217,7 +217,7 @@ macro isdefined(s::Symbol) end # return an integer such that object_id(x)==object_id(y) if x===y -object_id(x::ANY) = ccall(:jl_object_id, UInt, (Any,), x) +object_id(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) struct DataTypeLayout nfields::UInt32 @@ -258,7 +258,7 @@ julia> isimmutable([1,2]) false ``` """ -isimmutable(x::ANY) = (@_pure_meta; (isa(x,Tuple) || !typeof(x).mutable)) +isimmutable(@nospecialize(x)) = (@_pure_meta; (isa(x,Tuple) || !typeof(x).mutable)) isstructtype(t::DataType) = (@_pure_meta; length(t.types) != 0 || (t.size==0 && !t.abstract)) isstructtype(x) = (@_pure_meta; false) @@ -303,7 +303,7 @@ julia> isleaftype(Vector{Complex{Float32}}) true ``` """ -isleaftype(t::ANY) = (@_pure_meta; isa(t, DataType) && t.isleaftype) +isleaftype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isleaftype) """ Base.isabstract(T) @@ -320,7 +320,7 @@ julia> Base.isabstract(Vector) false ``` """ -function isabstract(t::ANY) +function isabstract(@nospecialize(t)) @_pure_meta t = unwrap_unionall(t) isa(t,DataType) && t.abstract @@ -355,8 +355,8 @@ end Compute a type that contains the intersection of `T` and `S`. Usually this will be the smallest such type or one close to it. """ -typeintersect(a::ANY,b::ANY) = (@_pure_meta; ccall(:jl_type_intersection, Any, (Any,Any), a, b)) -typeseq(a::ANY,b::ANY) = (@_pure_meta; a<:b && b<:a) +typeintersect(@nospecialize(a),@nospecialize(b)) = (@_pure_meta; ccall(:jl_type_intersection, Any, (Any,Any), a, b)) +typeseq(@nospecialize(a),@nospecialize(b)) = (@_pure_meta; a<:b && b<:a) """ fieldoffset(type, i) @@ -440,7 +440,7 @@ type_alignment(x::DataType) = (@_pure_meta; ccall(:jl_get_alignment, Csize_t, (A Get the number of fields that an instance of the given type would have. An error is thrown if the type is too abstract to determine this. """ -function fieldcount(t::ANY) +function fieldcount(@nospecialize t) if t isa UnionAll || t isa Union t = ccall(:jl_argument_datatype, Any, (Any,), t) if t === nothing @@ -539,7 +539,7 @@ julia> subtypes(Integer) """ subtypes(x::Union{DataType,UnionAll}) = subtypes(Main, x) -function to_tuple_type(t::ANY) +function to_tuple_type(@nospecialize(t)) @_pure_meta if isa(t,Tuple) || isa(t,AbstractArray) || isa(t,SimpleVector) t = Tuple{t...} @@ -554,14 +554,14 @@ function to_tuple_type(t::ANY) t end -tt_cons(t::ANY, tup::ANY) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.parameters : tup)...}) +tt_cons(@nospecialize(t), @nospecialize(tup)) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.parameters : tup)...}) """ code_lowered(f, types) Returns an array of lowered ASTs for the methods matching the given generic function and type signature. """ -function code_lowered(f::ANY, t::ANY=Tuple) +function code_lowered(@nospecialize(f), @nospecialize t = Tuple) asts = map(methods(f, t)) do m return uncompressed_ast(m::Method) end @@ -571,22 +571,22 @@ end # low-level method lookup functions used by the compiler unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) -unionlen(x::ANY) = 1 +unionlen(@nospecialize(x)) = 1 _uniontypes(x::Union, ts) = (_uniontypes(x.a,ts); _uniontypes(x.b,ts); ts) -_uniontypes(x::ANY, ts) = (push!(ts, x); ts) -uniontypes(x::ANY) = _uniontypes(x, Any[]) +_uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) +uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) -function _methods(f::ANY, t::ANY, lim::Int, world::UInt) +function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) ft = isa(f,Type) ? Type{f} : typeof(f) tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} return _methods_by_ftype(tt, lim, world) end -function _methods_by_ftype(t::ANY, lim::Int, world::UInt) +function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt) return _methods_by_ftype(t, lim, world, UInt[typemin(UInt)], UInt[typemax(UInt)]) end -function _methods_by_ftype(t::ANY, lim::Int, world::UInt, min::Array{UInt,1}, max::Array{UInt,1}) +function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt, min::Array{UInt,1}, max::Array{UInt,1}) return ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), t, lim, 0, world, min, max) end @@ -619,7 +619,7 @@ Returns the method table for `f`. If `types` is specified, returns an array of methods whose types match. """ -function methods(f::ANY, t::ANY) +function methods(@nospecialize(f), @nospecialize(t)) if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) end @@ -630,7 +630,7 @@ end methods(f::Core.Builtin) = MethodList(Method[], typeof(f).name.mt) -function methods_including_ambiguous(f::ANY, t::ANY) +function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) ft = isa(f,Type) ? Type{f} : typeof(f) tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} world = typemax(UInt) @@ -639,7 +639,7 @@ function methods_including_ambiguous(f::ANY, t::ANY) ms = ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), tt, -1, 1, world, min, max)::Array{Any,1} return MethodList(Method[m[3] for m in ms], typeof(f).name.mt) end -function methods(f::ANY) +function methods(@nospecialize(f)) # return all matches return methods(f, Tuple{Vararg{Any}}) end @@ -724,7 +724,7 @@ struct CodegenParams end # Printing code representations in IR and assembly -function _dump_function(f::ANY, t::ANY, native::Bool, wrapper::Bool, +function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool, strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol=:att, optimize::Bool=true, params::CodegenParams=CodegenParams()) ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") @@ -780,10 +780,10 @@ function and type signature to `io` which defaults to `STDOUT`. All metadata and dbg.* calls are removed from the printed bitcode. Use code_llvm_raw for the full IR. """ -code_llvm(io::IO, f::ANY, types::ANY=Tuple, strip_ir_metadata=true, dump_module=false) = +code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Tuple), strip_ir_metadata=true, dump_module=false) = print(io, _dump_function(f, types, false, false, strip_ir_metadata, dump_module)) -code_llvm(f::ANY, types::ANY=Tuple) = code_llvm(STDOUT, f, types) -code_llvm_raw(f::ANY, types::ANY=Tuple) = code_llvm(STDOUT, f, types, false) +code_llvm(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types) +code_llvm_raw(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types, false) """ code_native([io], f, types, [syntax]) @@ -792,13 +792,13 @@ Prints the native assembly instructions generated for running the method matchin generic function and type signature to `io` which defaults to `STDOUT`. Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. Output is AT&T syntax by default. """ -code_native(io::IO, f::ANY, types::ANY=Tuple, syntax::Symbol=:att) = +code_native(io::IO, @nospecialize(f), @nospecialize(types=Tuple), syntax::Symbol=:att) = print(io, _dump_function(f, types, true, false, false, false, syntax)) -code_native(f::ANY, types::ANY=Tuple, syntax::Symbol=:att) = code_native(STDOUT, f, types, syntax) -code_native(::IO, ::ANY, ::Symbol) = error("illegal code_native call") # resolve ambiguous call +code_native(@nospecialize(f), @nospecialize(types=Tuple), syntax::Symbol=:att) = code_native(STDOUT, f, types, syntax) +code_native(::IO, ::Any, ::Symbol) = error("illegal code_native call") # resolve ambiguous call # give a decent error message if we try to instantiate a staged function on non-leaf types -function func_for_method_checked(m::Method, types::ANY) +function func_for_method_checked(m::Method, @nospecialize types) if isdefined(m,:generator) && !isdefined(m,:source) && !isleaftype(types) error("cannot call @generated function `", m, "` ", "with abstract argument types: ", types) @@ -813,7 +813,7 @@ Returns an array of lowered and type-inferred ASTs for the methods matching the generic function and type signature. The keyword argument `optimize` controls whether additional optimizations, such as inlining, are also applied. """ -function code_typed(f::ANY, types::ANY=Tuple; optimize=true) +function code_typed(@nospecialize(f), @nospecialize(types=Tuple); optimize=true) ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) @@ -831,7 +831,7 @@ function code_typed(f::ANY, types::ANY=Tuple; optimize=true) return asts end -function return_types(f::ANY, types::ANY=Tuple) +function return_types(@nospecialize(f), @nospecialize(types=Tuple)) ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) @@ -856,7 +856,7 @@ Returns the method of `f` (a `Method` object) that would be called for arguments If `types` is an abstract type, then the method that would be called by `invoke` is returned. """ -function which(f::ANY, t::ANY) +function which(@nospecialize(f), @nospecialize(t)) if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) end @@ -923,9 +923,9 @@ end Returns a tuple `(filename,line)` giving the location of a generic `Function` definition. """ -functionloc(f::ANY, types::ANY) = functionloc(which(f,types)) +functionloc(@nospecialize(f), @nospecialize(types)) = functionloc(which(f,types)) -function functionloc(f::ANY) +function functionloc(@nospecialize(f)) mt = methods(f) if isempty(mt) if isa(f, Function) @@ -953,7 +953,7 @@ function_module(f::Function) = datatype_module(typeof(f)) Determine the module containing a given definition of a generic function. """ -function function_module(f::ANY, types::ANY) +function function_module(@nospecialize(f), @nospecialize(types)) m = methods(f, types) if isempty(m) error("no matching methods") @@ -973,7 +973,7 @@ julia> method_exists(length, Tuple{Array}) true ``` """ -function method_exists(f::ANY, t::ANY, world=typemax(UInt)) +function method_exists(@nospecialize(f), @nospecialize(t), world=typemax(UInt)) t = to_tuple_type(t) t = Tuple{isa(f,Type) ? Type{f} : typeof(f), t.parameters...} return ccall(:jl_method_exists, Cint, (Any, Any, UInt), typeof(f).name.mt, t, world) != 0 diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index f6208db8878835..ca6c38afabe6aa 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -53,7 +53,7 @@ mutable struct REPLBackend new(repl_channel, response_channel, in_eval) end -function eval_user_input(ast::ANY, backend::REPLBackend) +function eval_user_input(@nospecialize(ast), backend::REPLBackend) iserr, lasterr = false, ((), nothing) Base.sigatomic_begin() while true @@ -125,11 +125,11 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x) end display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x) -function print_response(repl::AbstractREPL, val::ANY, bt, show_value::Bool, have_color::Bool) +function print_response(repl::AbstractREPL, @nospecialize(val), bt, show_value::Bool, have_color::Bool) repl.waserror = bt !== nothing print_response(outstream(repl), val, bt, show_value, have_color, specialdisplay(repl)) end -function print_response(errio::IO, val::ANY, bt, show_value::Bool, have_color::Bool, specialdisplay=nothing) +function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, have_color::Bool, specialdisplay=nothing) Base.sigatomic_begin() while true try diff --git a/base/serialize.jl b/base/serialize.jl index 5c1c7d1b0e21e9..6a6847a850e90a 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -70,7 +70,7 @@ const ser_version = 6 # do not make changes without bumping the version #! const NTAGS = length(TAGS) -function sertag(v::ANY) +function sertag(@nospecialize(v)) ptr = pointer_from_objref(v) ptags = convert(Ptr{Ptr{Void}}, pointer(TAGS)) # note: constant ints & reserved slots never returned here @@ -149,7 +149,7 @@ function serialize_cycle(s::AbstractSerializer, x) return false end -function serialize_cycle_header(s::AbstractSerializer, x::ANY) +function serialize_cycle_header(s::AbstractSerializer, @nospecialize(x)) serialize_cycle(s, x) && return true serialize_type(s, typeof(x), true) return false @@ -359,7 +359,7 @@ end # TODO: make this bidirectional, so objects can be sent back via the same key const object_numbers = WeakKeyDict() const obj_number_salt = Ref(0) -function object_number(l::ANY) +function object_number(@nospecialize(l)) global obj_number_salt, object_numbers if haskey(object_numbers, l) return object_numbers[l] @@ -374,13 +374,13 @@ end lookup_object_number(s::AbstractSerializer, n::UInt64) = nothing -remember_object(s::AbstractSerializer, o::ANY, n::UInt64) = nothing +remember_object(s::AbstractSerializer, @nospecialize(o), n::UInt64) = nothing function lookup_object_number(s::SerializationState, n::UInt64) return get(s.known_object_data, n, nothing) end -function remember_object(s::SerializationState, o::ANY, n::UInt64) +function remember_object(s::SerializationState, @nospecialize(o), n::UInt64) s.known_object_data[n] = o return nothing end @@ -608,9 +608,9 @@ function serialize(s::AbstractSerializer, u::UnionAll) end end -serialize(s::AbstractSerializer, x::ANY) = serialize_any(s, x) +serialize(s::AbstractSerializer, @nospecialize(x)) = serialize_any(s, x) -function serialize_any(s::AbstractSerializer, x::ANY) +function serialize_any(s::AbstractSerializer, @nospecialize(x)) tag = sertag(x) if tag > 0 return write_as_tag(s.io, tag) @@ -647,7 +647,7 @@ function deserialize(s::AbstractSerializer) handle_deserialize(s, Int32(read(s.io, UInt8)::UInt8)) end -function deserialize_cycle(s::AbstractSerializer, x::ANY) +function deserialize_cycle(s::AbstractSerializer, @nospecialize(x)) slot = pop!(s.pending_refs) s.table[slot] = x nothing @@ -658,7 +658,7 @@ end # push!(s.pending_refs, slot) # slot = pop!(s.pending_refs) # s.table[slot] = x -function resolve_ref_immediately(s::AbstractSerializer, x::ANY) +function resolve_ref_immediately(s::AbstractSerializer, @nospecialize(x)) s.table[s.counter] = x s.counter += 1 nothing diff --git a/base/show.jl b/base/show.jl index fe4ad307a1ab41..c060839f7040ee 100644 --- a/base/show.jl +++ b/base/show.jl @@ -107,8 +107,8 @@ get(io::IO, key, default) = default displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displaysize(io.io) -show_circular(io::IO, x::ANY) = false -function show_circular(io::IOContext, x::ANY) +show_circular(io::IO, @nospecialize(x)) = false +function show_circular(io::IOContext, @nospecialize(x)) d = 1 for (k, v) in io.dict if k === :SHOWN_SET @@ -122,8 +122,8 @@ function show_circular(io::IOContext, x::ANY) return false end -show(io::IO, x::ANY) = show_default(io, x) -function show_default(io::IO, x::ANY) +show(io::IO, @nospecialize(x)) = show_default(io, x) +function show_default(io::IO, @nospecialize(x)) t = typeof(x)::DataType show(io, t) print(io, '(') @@ -191,7 +191,7 @@ function show(io::IO, x::Union) show_comma_array(io, sorted_types, '{', '}') end -function print_without_params(x::ANY) +function print_without_params(@nospecialize(x)) if isa(x,UnionAll) b = unwrap_unionall(x) return isa(b,DataType) && b.name.wrapper === x @@ -520,7 +520,7 @@ typeemphasize(io::IO) = get(io, :TYPEEMPHASIZE, false) === true const indent_width = 4 -function show_expr_type(io::IO, ty::ANY, emph::Bool) +function show_expr_type(io::IO, @nospecialize(ty), emph::Bool) if ty === Function print(io, "::F") elseif ty === Core.IntrinsicFunction @@ -1099,7 +1099,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type) nothing end -resolvebinding(ex::ANY) = ex +resolvebinding(@nospecialize(ex)) = ex resolvebinding(ex::QuoteNode) = ex.value resolvebinding(ex::Symbol) = resolvebinding(GlobalRef(Main, ex)) function resolvebinding(ex::Expr) @@ -1132,7 +1132,7 @@ function show(io::IO, tv::TypeVar) # Otherwise, the lower bound should be printed if it is not `Bottom` # and the upper bound should be printed if it is not `Any`. in_env = (:unionall_env => tv) in io - function show_bound(io::IO, b::ANY) + function show_bound(io::IO, @nospecialize(b)) parens = isa(b,UnionAll) && !print_without_params(b) parens && print(io, "(") show(io, b) @@ -1179,7 +1179,7 @@ function dump(io::IO, x::SimpleVector, n::Int, indent) nothing end -function dump(io::IO, x::ANY, n::Int, indent) +function dump(io::IO, @nospecialize(x), n::Int, indent) T = typeof(x) if isa(x, Function) print(io, x, " (function of type ", T, ")") @@ -1272,7 +1272,7 @@ end # dumptype is for displaying abstract type hierarchies, # based on Jameson Nash's examples/typetree.jl -function dumptype(io::IO, x::ANY, n::Int, indent) +function dumptype(io::IO, @nospecialize(x), n::Int, indent) print(io, x) n == 0 && return # too deeply nested isa(x, DataType) && x.abstract && dumpsubtypes(io, x, Main, n, indent) diff --git a/base/summarysize.jl b/base/summarysize.jl index 133e6a5edfbb28..414b94b90d02e8 100644 --- a/base/summarysize.jl +++ b/base/summarysize.jl @@ -8,7 +8,7 @@ struct SummarySize chargeall::Any end -_nfields(x::ANY) = length(typeof(x).types) +_nfields(@nospecialize x) = length(typeof(x).types) """ Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int @@ -20,9 +20,10 @@ Compute the amount of memory used by all unique objects reachable from the argum - `chargeall`: specifies the types of objects to always charge the size of all of their fields, even if those fields would normally be excluded. """ -function summarysize(obj::ANY; - exclude::ANY = Union{DataType, TypeName, Method}, - chargeall::ANY = Union{TypeMapEntry, Core.MethodInstance}) +function summarysize(obj; + exclude = Union{DataType, TypeName, Method}, + chargeall = Union{TypeMapEntry, Core.MethodInstance}) + @nospecialize obj exclude chargeall ss = SummarySize(ObjectIdDict(), Any[], Int[], exclude, chargeall) size::Int = ss(obj) while !isempty(ss.frontier_x) @@ -61,9 +62,9 @@ function summarysize(obj::ANY; return size end -(ss::SummarySize)(obj::ANY) = _summarysize(ss, obj) +(ss::SummarySize)(@nospecialize obj) = _summarysize(ss, obj) # define the general case separately to make sure it is not specialized for every type -@noinline function _summarysize(ss::SummarySize, obj::ANY) +@noinline function _summarysize(ss::SummarySize, @nospecialize obj) key = pointer_from_objref(obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) if _nfields(obj) > 0 diff --git a/base/sysimg.jl b/base/sysimg.jl index aaad45cc637271..826f48e61f5bbc 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -64,9 +64,9 @@ if false # goes wrong during bootstrap before printing code is available. # otherwise, they just just eventually get (noisily) overwritten later global show, print, println - show(io::IO, x::ANY) = Core.show(io, x) - print(io::IO, a::ANY...) = Core.print(io, a...) - println(io::IO, x::ANY...) = Core.println(io, x...) + show(io::IO, x) = Core.show(io, x) + print(io::IO, a...) = Core.print(io, a...) + println(io::IO, x...) = Core.println(io, x...) end ## Load essential files and libraries diff --git a/base/test.jl b/base/test.jl index 7b03312ae4e384..ccd9e747e423a9 100644 --- a/base/test.jl +++ b/base/test.jl @@ -455,7 +455,7 @@ end # An internal function, called by the code generated by @test_throws # to evaluate and catch the thrown exception - if it exists -function do_test_throws(result::ExecutionResult, orig_expr::ANY, extype::ANY) +function do_test_throws(result::ExecutionResult, @nospecialize(orig_expr), @nospecialize(extype)) if isa(result, Threw) # Check that the right type of exception was thrown success = false diff --git a/base/tuple.jl b/base/tuple.jl index eb035a1e882c7c..cce81f01518305 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -219,7 +219,7 @@ end _totuple(::Type{Tuple{}}, itr, s) = () -function _totuple_err(T::ANY) +function _totuple_err(@nospecialize T) @_noinline_meta throw(ArgumentError("too few elements for tuple type $T")) end diff --git a/doc/src/devdocs/functions.md b/doc/src/devdocs/functions.md index 70c509ec341d28..9f283dc8d10f0e 100644 --- a/doc/src/devdocs/functions.md +++ b/doc/src/devdocs/functions.md @@ -223,8 +223,8 @@ Performance-critical higher-order functions like `map` certainly call their argu and so will still be specialized as expected. This optimization is implemented by recording which arguments are called during the `analyze-variables` pass in the front end. When `cache_method` sees an argument in the `Function` type hierarchy passed to a slot declared as `Any` or `Function`, -it pretends the slot was declared as `ANY` (the "don't specialize" hint). This heuristic seems -to be extremely effective in practice. +it behaves as if the `@nospecialize` annotation were applied. This heuristic seems to be extremely +effective in practice. The next issue concerns the structure of method cache hash tables. Empirical studies show that the vast majority of dynamically-dispatched calls involve one or two arguments. In turn, many diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index fd97c0a598010e..bb5ffc0c596dab 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -138,6 +138,7 @@ Base.esc Base.@inbounds Base.@inline Base.@noinline +Base.@nospecialize Base.gensym Base.@gensym Base.@polly diff --git a/doc/src/stdlib/constants.md b/doc/src/stdlib/constants.md index f5174fddc42d41..e88211b354038c 100644 --- a/doc/src/stdlib/constants.md +++ b/doc/src/stdlib/constants.md @@ -8,7 +8,6 @@ Base.C_NULL Base.VERSION Base.LOAD_PATH Base.JULIA_HOME -Core.ANY Base.Sys.CPU_CORES Base.Sys.WORD_SIZE Base.Sys.KERNEL diff --git a/examples/typetree.jl b/examples/typetree.jl index 1ca87148332910..7d614f71870158 100644 --- a/examples/typetree.jl +++ b/examples/typetree.jl @@ -17,7 +17,7 @@ struct TTNode typ # ::Type subtypes::Dict{Binding, TTNode} - TTNode(t::ANY) = new(t, Dict{Binding, TTNode}()) + TTNode(@nospecialize t) = new(t, Dict{Binding, TTNode}()) end # Add a node to a dict if not added @@ -44,7 +44,7 @@ function store_type(sname::Binding, t::Union) return tnode.subtypes end -function store_union(sname::Binding, tnode::TTNode, t::ANY) +function store_union(sname::Binding, tnode::TTNode, @nospecialize t) t = Base.unwrap_unionall(t) if isa(t, Union) store_union(sname, tnode, t.a) diff --git a/src/ast.c b/src/ast.c index 5ada8cef0d86a6..33292877a069ee 100644 --- a/src/ast.c +++ b/src/ast.c @@ -56,7 +56,7 @@ jl_sym_t *inert_sym; jl_sym_t *vararg_sym; jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym; jl_sym_t *polly_sym; jl_sym_t *inline_sym; jl_sym_t *propagate_inbounds_sym; -jl_sym_t *isdefined_sym; +jl_sym_t *isdefined_sym; jl_sym_t *nospecialize_sym; static uint8_t flisp_system_image[] = { #include @@ -433,6 +433,7 @@ void jl_init_frontend(void) inline_sym = jl_symbol("inline"); propagate_inbounds_sym = jl_symbol("propagate_inbounds"); isdefined_sym = jl_symbol("isdefined"); + nospecialize_sym = jl_symbol("nospecialize"); } JL_DLLEXPORT void jl_lisp_prompt(void) diff --git a/src/ast.scm b/src/ast.scm index 7502ebb897ab6f..0023531f043375 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -152,6 +152,10 @@ (if (not (symbol? (cadr v))) (bad-formal-argument (cadr v))) (decl-var v)) + ((meta) ;; allow certain per-argument annotations + (if (nospecialize-meta? v #t) + (arg-name (caddr v)) + (bad-formal-argument v))) (else (bad-formal-argument v)))))) (define (arg-type v) @@ -167,6 +171,10 @@ (if (not (symbol? (cadr v))) (bad-formal-argument (cadr v))) (decl-type v)) + ((meta) ;; allow certain per-argument annotations + (if (nospecialize-meta? v #t) + (arg-type (caddr v)) + (bad-formal-argument v))) (else (bad-formal-argument v)))))) ;; convert a lambda list into a list of just symbols @@ -310,6 +318,10 @@ (define (kwarg? e) (and (pair? e) (eq? (car e) 'kw))) +(define (nospecialize-meta? e (one #f)) + (and (if one (length= e 3) (length> e 2)) + (eq? (car e) 'meta) (eq? (cadr e) 'nospecialize))) + ;; flatten nested expressions with the given head ;; (op (op a b) c) => (op a b c) (define (flatten-ex head e) diff --git a/src/gf.c b/src/gf.c index 64bcc801cc7b8a..3cdbb33b4c18b4 100644 --- a/src/gf.c +++ b/src/gf.c @@ -17,7 +17,7 @@ #include #endif -// ::ANY has no effect if the number of overlapping methods is greater than this +// @nospecialize has no effect if the number of overlapping methods is greater than this #define MAX_UNSPECIALIZED_CONFLICTS 32 #ifdef __cplusplus @@ -610,8 +610,9 @@ static void jl_cacheable_sig( int notcalled_func = (i > 0 && i <= 8 && !(definition->called & (1 << (i - 1))) && jl_subtype(elt, (jl_value_t*)jl_function_type)); - if (decl_i == jl_ANY_flag) { - // don't specialize on slots marked ANY + if (i > 0 && i <= sizeof(definition->nospecialize) * 8 && + (definition->nospecialize & (1 << (i - 1))) && + decl_i == (jl_value_t*)jl_any_type) { // TODO: nospecialize with other types if (!*newparams) *newparams = jl_svec_copy(type->parameters); jl_svecset(*newparams, i, (jl_value_t*)jl_any_type); *need_guard_entries = 1; @@ -666,7 +667,7 @@ static void jl_cacheable_sig( !jl_has_free_typevars(decl_i)) { /* here's a fairly simple heuristic: if this argument slot's - declared type is general (Type, Any, or ANY), + declared type is general (Type or Any), then don't specialize for every Type that got passed. Since every type x has its own type Type{x}, this would be @@ -713,9 +714,10 @@ JL_DLLEXPORT int jl_is_cacheable_sig( continue; if (jl_is_kind(elt)) // kind slots always need guard entries (checking for subtypes of Type) continue; - if (decl_i == jl_ANY_flag) { - // don't specialize on slots marked ANY - if (elt != (jl_value_t*)jl_any_type && elt != jl_ANY_flag) + if (i > 0 && i <= sizeof(definition->nospecialize) * 8 && + (definition->nospecialize & (1 << (i - 1))) && + decl_i == (jl_value_t*)jl_any_type) { // TODO: nospecialize with other types + if (elt != (jl_value_t*)jl_any_type) return 0; continue; } @@ -793,7 +795,7 @@ JL_DLLEXPORT int jl_is_cacheable_sig( !jl_has_free_typevars(decl_i)) { /* here's a fairly simple heuristic: if this argument slot's - declared type is general (Type, Any, or ANY), + declared type is general (Type or Any), then don't specialize for every Type that got passed. Since every type x has its own type Type{x}, this would be @@ -2257,16 +2259,6 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio break; } } - // don't analyze slots declared with ANY - // TODO - /* - l = jl_nparams(ml->sig); - size_t m = jl_nparams(ti); - for(i=0; i < l && i < m; i++) { - if (jl_tparam(ml->sig, i) == jl_ANY_flag) - jl_tupleset(ti, i, jl_any_type); - } - */ } if (!skip) { /* diff --git a/src/jltypes.c b/src/jltypes.c index acbb2bdbb3c268..45a7c25a92e50d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -145,7 +145,6 @@ static int typeenv_has(jl_typeenv_t *env, jl_tvar_t *v) static int has_free_typevars(jl_value_t *v, jl_typeenv_t *env) { if (jl_typeis(v, jl_tvar_type)) { - if (v == jl_ANY_flag) return 0; return !typeenv_has(env, (jl_tvar_t*)v); } if (jl_is_uniontype(v)) @@ -180,7 +179,6 @@ JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) static void find_free_typevars(jl_value_t *v, jl_typeenv_t *env, jl_array_t *out) { if (jl_typeis(v, jl_tvar_type)) { - if (v == jl_ANY_flag) return; if (!typeenv_has(env, (jl_tvar_t*)v)) jl_array_ptr_1d_push(out, v); } @@ -237,7 +235,7 @@ static int jl_has_bound_typevars(jl_value_t *v, jl_typeenv_t *env) return ans; } if (jl_is_datatype(v)) { - if (!((jl_datatype_t*)v)->hasfreetypevars && !(env && env->var == (jl_tvar_t*)jl_ANY_flag)) + if (!((jl_datatype_t*)v)->hasfreetypevars) return 0; size_t i; for (i=0; i < jl_nparams(v); i++) { @@ -668,8 +666,6 @@ static int is_cacheable(jl_datatype_t *type) assert(jl_is_datatype(type)); jl_svec_t *t = type->parameters; if (jl_svec_len(t) == 0) return 0; - if (jl_has_typevar((jl_value_t*)type, (jl_tvar_t*)jl_ANY_flag)) - return 0; // cache abstract types with no free type vars if (jl_is_abstracttype(type)) return !jl_has_free_typevars((jl_value_t*)type); @@ -1920,7 +1916,7 @@ void jl_init_types(void) jl_method_type = jl_new_datatype(jl_symbol("Method"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(18, + jl_perm_symsvec(19, "name", "module", "file", @@ -1937,9 +1933,10 @@ void jl_init_types(void) "invokes", "nargs", "called", + "nospecialize", "isva", "pure"), - jl_svec(18, + jl_svec(19, jl_sym_type, jl_module_type, jl_sym_type, @@ -1956,6 +1953,7 @@ void jl_init_types(void) jl_any_type, jl_int32_type, jl_int32_type, + jl_int32_type, jl_bool_type, jl_bool_type), 0, 1, 9); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 52cf9fd004375e..f671b97021bf0c 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -378,6 +378,11 @@ (define (keywords-method-def-expr name sparams argl body isstaged rett) (let* ((kargl (cdar argl)) ;; keyword expressions (= k v) + (annotations (map (lambda (a) `(meta nospecialize ,(arg-name (cadr (caddr a))))) + (filter nospecialize-meta? kargl))) + (kargl (map (lambda (a) + (if (nospecialize-meta? a) (caddr a) a)) + kargl)) (pargl (cdr argl)) ;; positional args (body (if (and (pair? body) (eq? (car body) 'block)) body @@ -463,8 +468,10 @@ (decl-var (car not-optional))) (car not-optional)) ,@(cdr not-optional) ,@vararg) - `(block - ,@stmts) isstaged rett) + (insert-after-meta `(block + ,@stmts) + annotations) + isstaged rett) ;; call with unsorted keyword args. this sorts and re-dispatches. ,(method-def-expr- @@ -621,8 +628,9 @@ argl)) (define (check-kw-args kw) - (let ((invalid (filter (lambda (x) (not (or (kwarg? x) - (vararg? x)))) + (let ((invalid (filter (lambda (x) (not (or (kwarg? x) (vararg? x) + (and (nospecialize-meta? x) + (or (kwarg? (caddr x)) (vararg? (caddr x))))))) kw))) (if (pair? invalid) (if (and (pair? (car invalid)) (eq? 'parameters (caar invalid))) @@ -1026,6 +1034,12 @@ (string "function Base.broadcast(::typeof(" (deparse op_) "), ...)"))) op_)) (name (if op '(|.| Base (inert broadcast)) name)) + (annotations (map (lambda (a) `(meta nospecialize ,(arg-name a))) + (filter nospecialize-meta? argl))) + (body (insert-after-meta (caddr e) annotations)) + (argl (map (lambda (a) + (if (nospecialize-meta? a) (caddr a) a)) + argl)) (argl (if op (cons `(|::| (call (core Typeof) ,op)) argl) argl)) (sparams (map analyze-typevar (cond (has-sp (cddr head)) (where where) @@ -1046,7 +1060,7 @@ (name (if (or (decl? name) (and (pair? name) (eq? (car name) 'curly))) #f name))) (expand-forms - (method-def-expr name sparams argl (caddr e) isstaged rett)))) + (method-def-expr name sparams argl body isstaged rett)))) (else (error (string "invalid assignment location \"" (deparse name) "\"")))))) @@ -1170,7 +1184,7 @@ (|::| __module__ (core Module)) ,@(map (lambda (v) (if (symbol? v) - `(|::| ,v (core ANY)) + `(meta nospecialize ,v) v)) anames)) ,@(cddr e))))) @@ -2898,13 +2912,15 @@ f(x) = yt(x) ;; return `body` with `stmts` inserted after any meta nodes (define (insert-after-meta body stmts) - (let ((meta (take-while (lambda (x) (and (pair? x) - (memq (car x) '(line meta)))) - (cdr body)))) - `(,(car body) - ,@meta - ,@stmts - ,@(list-tail body (+ 1 (length meta)))))) + (if (null? stmts) + body + (let ((meta (take-while (lambda (x) (and (pair? x) + (memq (car x) '(line meta)))) + (cdr body)))) + `(,(car body) + ,@meta + ,@stmts + ,@(list-tail body (+ 1 (length meta))))))) (define (add-box-inits-to-body lam body) (let ((args (map arg-name (lam:args lam))) @@ -3780,6 +3796,9 @@ f(x) = yt(x) ((and (pair? e) (eq? (car e) 'outerref)) (let ((idx (get sp-table (cadr e) #f))) (if idx `(static_parameter ,idx) (cadr e)))) + ((nospecialize-meta? e) + ;; convert nospecialize vars to slot numbers + `(meta nospecialize ,@(map renumber-slots (cddr e)))) ((or (atom? e) (quoted? e)) e) ((ssavalue? e) (let ((idx (or (get ssavalue-table (cadr e) #f) diff --git a/src/julia.h b/src/julia.h index 652267ae81619e..92fadc12c852c4 100644 --- a/src/julia.h +++ b/src/julia.h @@ -254,7 +254,8 @@ typedef struct _jl_method_t { union jl_typemap_t invokes; int32_t nargs; - int32_t called; // bit flags: whether each of the first 8 arguments is called + int32_t called; // bit flags: whether each of the first 8 arguments is called + int32_t nospecialize; // bit flags: which arguments should not be specialized uint8_t isva; uint8_t pure; diff --git a/src/julia_internal.h b/src/julia_internal.h index 864cf7ec996041..6b7fc807112a85 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -994,7 +994,7 @@ extern jl_sym_t *meta_sym; extern jl_sym_t *list_sym; extern jl_sym_t *inert_sym; extern jl_sym_t *static_parameter_sym; extern jl_sym_t *polly_sym; extern jl_sym_t *inline_sym; extern jl_sym_t *propagate_inbounds_sym; -extern jl_sym_t *isdefined_sym; +extern jl_sym_t *isdefined_sym; extern jl_sym_t *nospecialize_sym; void jl_register_fptrs(uint64_t sysimage_base, const char *base, const int32_t *offsets, jl_method_instance_t **linfos, size_t n); diff --git a/src/macroexpand.scm b/src/macroexpand.scm index 1031d0f54060f1..e63847e29080a7 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -192,6 +192,10 @@ (case (car v) ((... kw |::|) (try-arg-name (cadr v))) ((escape) (list v)) + ((meta) ;; allow certain per-argument annotations + (if (nospecialize-meta? v #t) + (try-arg-name (caddr v)) + '())) (else '()))))) ;; get names from a formal argument list, specifying whether to include escaped ones diff --git a/src/method.c b/src/method.c index bcf9c2428cf30b..a0ba1c889b526c 100644 --- a/src/method.c +++ b/src/method.c @@ -434,6 +434,29 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) set_lineno = 1; } } + else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == meta_sym && + jl_expr_nargs(st) > 1 && jl_exprarg(st, 0) == (jl_value_t*)nospecialize_sym) { + for (size_t j=1; j < jl_expr_nargs(st); j++) { + jl_value_t *aj = jl_exprarg(st, j); + if (jl_is_slot(aj)) { + int sn = (int)jl_slot_number(aj) - 2; + if (sn >= 0) { // @nospecialize on self is valid but currently ignored + if (sn > (m->nargs - 2)) { + jl_error("@nospecialize annotation applied to a non-argument"); + } + else if (sn >= sizeof(m->nospecialize) * 8) { + jl_printf(JL_STDERR, + "WARNING: @nospecialize annotation only supported on the first %d arguments.\n", + (int)(sizeof(m->nospecialize) * 8)); + } + else { + m->nospecialize |= (1 << sn); + } + } + } + } + st = jl_nothing; + } else { st = jl_resolve_globals(st, m->module, sparam_vars); } @@ -465,6 +488,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->file = empty_sym; m->line = 0; m->called = 0xff; + m->nospecialize = 0; m->invokes.unknown = NULL; m->isva = 0; m->nargs = 0; @@ -644,6 +668,19 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt; jl_sym_t *name; jl_method_t *m = NULL; + size_t i, na = jl_svec_len(atypes); + int32_t nospec = 0; + for (i=1; i < na; i++) { + jl_value_t *ti = jl_svecref(atypes, i); + if (ti == jl_ANY_flag || + (jl_is_vararg_type(ti) && jl_tparam0(jl_unwrap_unionall(ti)) == jl_ANY_flag)) { + jl_depwarn("`x::ANY` is deprecated, use `@nospecialize(x)` instead.", + (jl_value_t*)jl_symbol("ANY")); + if (i <= 32) + nospec |= (1 << (i - 1)); + jl_svecset(atypes, i, jl_substitute_var(ti, (jl_tvar_t*)jl_ANY_flag, (jl_value_t*)jl_any_type)); + } + } jl_value_t *argtype = (jl_value_t*)jl_apply_tuple_type(atypes); JL_GC_PUSH3(&f, &m, &argtype); @@ -677,6 +714,7 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, } m = jl_new_method(f, name, module, (jl_tupletype_t*)argtype, nargs, isva, tvars, isstaged == jl_true); + m->nospecialize |= nospec; if (jl_has_free_typevars(argtype)) { jl_exceptionf(jl_argumenterror_type, @@ -688,7 +726,6 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_check_static_parameter_conflicts(m, f, tvars); - size_t i, na = jl_svec_len(atypes); for (i = 0; i < na; i++) { jl_value_t *elt = jl_svecref(atypes, i); if (!jl_is_type(elt) && !jl_is_typevar(elt)) { diff --git a/src/subtype.c b/src/subtype.c index 68aa745668fa98..f4a0e72ac900df 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -790,8 +790,6 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); // diagonal rule (record_var_occurrence). static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { - if (x == jl_ANY_flag) x = (jl_value_t*)jl_any_type; - if (y == jl_ANY_flag) y = (jl_value_t*)jl_any_type; if (jl_is_uniontype(x)) { if (x == y) return 1; x = pick_union_element(x, e, 0); @@ -1814,8 +1812,6 @@ static jl_value_t *intersect_type_type(jl_value_t *x, jl_value_t *y, jl_stenv_t static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { if (x == y) return y; - if (x == jl_ANY_flag) x = (jl_value_t*)jl_any_type; - if (y == jl_ANY_flag) y = (jl_value_t*)jl_any_type; if (jl_is_typevar(x)) { if (jl_is_typevar(y)) { jl_varbinding_t *xx = lookup(e, (jl_tvar_t*)x); @@ -2207,10 +2203,6 @@ JL_DLLEXPORT jl_svec_t *jl_env_from_type_intersection(jl_value_t *a, jl_value_t static int eq_msp(jl_value_t *a, jl_value_t *b, jl_typeenv_t *env) { - // equate ANY and Any for specificity purposes, #16153 - if ((a == (jl_value_t*)jl_any_type && b == jl_ANY_flag) || - (b == (jl_value_t*)jl_any_type && a == jl_ANY_flag)) - return 1; if (!(jl_is_type(a) || jl_is_typevar(a)) || !(jl_is_type(b) || jl_is_typevar(b))) return jl_egal(a, b); @@ -2496,8 +2488,8 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty } if (!invariant) { - if ((jl_datatype_t*)a == jl_any_type || a == jl_ANY_flag) return 0; - if ((jl_datatype_t*)b == jl_any_type || b == jl_ANY_flag) return 1; + if ((jl_datatype_t*)a == jl_any_type) return 0; + if ((jl_datatype_t*)b == jl_any_type) return 1; } if (jl_is_datatype(a) && jl_is_datatype(b)) { diff --git a/src/typemap.c b/src/typemap.c index d85a0fa8b4787e..3f8898e894d1b9 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -19,7 +19,7 @@ extern "C" { // compute whether the specificity of this type is equivalent to Any in the sort order static int jl_is_any(jl_value_t *t1) { - return (t1 == (jl_value_t*)jl_any_type || t1 == jl_ANY_flag || + return (t1 == (jl_value_t*)jl_any_type || (jl_is_typevar(t1) && ((jl_tvar_t*)t1)->ub == (jl_value_t*)jl_any_type)); } @@ -69,7 +69,7 @@ static int sig_match_by_type_simple(jl_value_t **types, size_t n, jl_tupletype_t return 0; } } - else if (decl == (jl_value_t*)jl_any_type || decl == jl_ANY_flag) { + else if (decl == (jl_value_t*)jl_any_type) { } else { if (jl_is_type_type(a)) // decl is not Type, because it would be caught above @@ -122,8 +122,7 @@ static inline int sig_match_simple(jl_value_t **args, size_t n, jl_value_t **sig for (i = 0; i < lensig; i++) { jl_value_t *decl = sig[i]; jl_value_t *a = args[i]; - if (decl == (jl_value_t*)jl_any_type || decl == jl_ANY_flag || - ((jl_value_t*)jl_typeof(a) == decl)) { + if (decl == (jl_value_t*)jl_any_type || ((jl_value_t*)jl_typeof(a) == decl)) { /* we are only matching concrete types here, and those types are hash-consed, so pointer comparison should work. @@ -820,7 +819,7 @@ jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *ml, jl_valu if (ml->guardsigs != jl_emptysvec) { for (i = 0, l = jl_svec_len(ml->guardsigs); i < l; i++) { // checking guard entries require a more - // expensive subtype check, since guard entries added for ANY might be + // expensive subtype check, since guard entries added for @nospecialize might be // abstract. this fixed issue #12967. if (jl_tuple_isa(args, n, (jl_tupletype_t*)jl_svecref(ml->guardsigs, i))) { goto nomatch; diff --git a/test/codegen.jl b/test/codegen.jl index da80ff1661f039..038645d5366bfd 100644 --- a/test/codegen.jl +++ b/test/codegen.jl @@ -7,7 +7,7 @@ const coverage = (Base.JLOptions().code_coverage > 0) || (Base.JLOptions().mallo const Iptr = sizeof(Int) == 8 ? "i64" : "i32" # `_dump_function` might be more efficient but it doesn't really matter here... -get_llvm(f::ANY, t::ANY, strip_ir_metadata=true, dump_module=false) = +get_llvm(@nospecialize(f), @nospecialize(t), strip_ir_metadata=true, dump_module=false) = sprint(code_llvm, f, t, strip_ir_metadata, dump_module) if opt_level > 0 diff --git a/test/core.jl b/test/core.jl index af8cad7ff14f56..6185f83acafefc 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1302,7 +1302,7 @@ mutable struct Baz4129 b::Bar4129 end -foo4129(a::Baz4129,c::Foo4129,b::Bar4129,x::ANY,y) = (a,b,c,x,y) +foo4129(a::Baz4129,c::Foo4129,b::Bar4129,@nospecialize(x),y) = (a,b,c,x,y) foo4129(a::Baz4129,b::Bar41291,args...) = foo4129(a,b.f,b,args...) foo4129(a::Baz4129,b::Bar41292,args...) = foo4129(a,b.f,b,args...) foo4129(a::Baz4129,args...) = foo4129(a,a.b,args...) @@ -3227,7 +3227,7 @@ cycle_in_solve_tvar_constraints{T}(::Type{T}, x::Val{T}) = 1 @test length(methods(cycle_in_solve_tvar_constraints)) == 2 # issue #12967 -foo12967(x, ::ANY) = 1 +foo12967(x, @nospecialize y) = 1 TupleType12967{T<:Tuple} = Type{T} foo12967(x, ::TupleType12967) = 2 @test foo12967(1, Int) == 1 @@ -3980,7 +3980,7 @@ function metadata_matches(ast::CodeInfo) @test boundscheck_cnt[] == 0 end -function test_metadata_matches(f::ANY, tt::ANY) +function test_metadata_matches(@nospecialize(f), @nospecialize(tt)) metadata_matches(code_typed(f, tt)[1][1]) end @@ -4188,16 +4188,16 @@ end # issue #16153 f16153(x) = 1 -f16153(x::ANY, y...) = 2 +f16153(@nospecialize(x), y...) = 2 @test f16153("") == 1 -ff16153(x::ANY, y...) = 2 +ff16153(@nospecialize(x), y...) = 2 ff16153(x) = 1 @test ff16153("") == 1 -g16153(x::ANY, y...) = 1 -g16153(x::ANY, y::ANY) = 2 +g16153(@nospecialize(x), y...) = 1 +g16153(@nospecialize(x), @nospecialize(y)) = 2 @test g16153(1, 1) == 2 -gg16153(x::ANY, y::ANY) = 2 -gg16153(x::ANY, y...) = 1 +gg16153(@nospecialize(x), @nospecialize(y)) = 2 +gg16153(@nospecialize(x), y...) = 1 @test gg16153(1, 1) == 2 # don't remove global variable accesses even if we "know" their type @@ -4555,7 +4555,7 @@ bad_tvars{T}() = 1 # issue #19059 - test for lowering of `let` with assignment not adding Box in simple cases contains_Box(e::GlobalRef) = (e.name === :Box) -contains_Box(e::ANY) = false +contains_Box(@nospecialize(e)) = false contains_Box(e::Expr) = any(contains_Box, e.args) function let_noBox() diff --git a/test/inference.jl b/test/inference.jl index 77b793c4ec720c..4d5b81b3e72921 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -270,7 +270,7 @@ for code in Any[ @code_typed(f18679())[1] @code_typed(g18679())[1]] @test all(x->isa(x, Type), code.slottypes) - local notconst(other::ANY) = true + local notconst(@nospecialize(other)) = true notconst(slot::TypedSlot) = @test isa(slot.typ, Type) function notconst(expr::Expr) @test isa(expr.typ, Type) @@ -443,7 +443,7 @@ function is_typed_expr(e::Expr) end return false end -test_inferred_static(other::ANY) = true +test_inferred_static(@nospecialize(other)) = true test_inferred_static(slot::TypedSlot) = @test isleaftype(slot.typ) function test_inferred_static(expr::Expr) if is_typed_expr(expr) @@ -664,7 +664,7 @@ end # issue #20704 f20704(::Int) = 1 -Base.@pure b20704(x::ANY) = f20704(x) +Base.@pure b20704(@nospecialize(x)) = f20704(x) @test b20704(42) === 1 @test_throws MethodError b20704(42.0) @@ -676,7 +676,7 @@ v20704() = Val{b20704(Any[1.0][1])} @test Base.return_types(v20704, ()) == Any[Type{Val{1}}] Base.@pure g20704(::Int) = 1 -h20704(x::ANY) = g20704(x) +h20704(@nospecialize(x)) = g20704(x) @test g20704(1) === 1 @test_throws MethodError h20704(1.2) @@ -957,7 +957,7 @@ g22364(x) = f22364(x, Any[[]][1]...) @test @inferred(g22364(1)) === 0 @test @inferred(g22364("1")) === 0.0 -function get_linfo(f::ANY, t::ANY) +function get_linfo(@nospecialize(f), @nospecialize(t)) if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) end @@ -974,7 +974,7 @@ function get_linfo(f::ANY, t::ANY) (Any, Any, Any, UInt), meth, tt, env, world) end -function test_const_return(f::ANY, t::ANY, val::ANY) +function test_const_return(@nospecialize(f), @nospecialize(t), @nospecialize(val)) linfo = get_linfo(f, t) # If coverage is not enabled, make the check strict by requiring constant ABI # Otherwise, check the typed AST to make sure we return a constant. diff --git a/test/keywordargs.jl b/test/keywordargs.jl index 0ac76fa7bcaafa..f978df865b4dbb 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -272,7 +272,7 @@ end @test g21147(((1,),), 2) === Int # issue #21510 -f21510(; a::ANY = 2) = a +f21510(; Base.@nospecialize a = 2) = a @test f21510(a=:b) == :b @test f21510() == 2 diff --git a/test/meta.jl b/test/meta.jl index 6e12549ab10ee2..c844c780500f5e 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -144,3 +144,21 @@ baremodule B end @test B.x == 3 @test B.M.x == 4 + +# specialization annotations + +function _nospec_some_args(@nospecialize(x), y, @nospecialize z::Int) +end +@test first(methods(_nospec_some_args)).nospecialize == 5 +@test first(methods(_nospec_some_args)).sig == Tuple{typeof(_nospec_some_args),Any,Any,Int} +function _nospec_some_args2(x, y, z) + @nospecialize x y + return 0 +end +@test first(methods(_nospec_some_args2)).nospecialize == 3 +function _nospec_with_default(@nospecialize x = 1) + 2x +end +@test collect(methods(_nospec_with_default))[2].nospecialize == 1 +@test _nospec_with_default() == 2 +@test _nospec_with_default(10) == 20 diff --git a/test/reflection.jl b/test/reflection.jl index 01802de56129f7..8b285e4958a399 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -395,7 +395,7 @@ end used_dup_var_tested15714 = false used_unique_var_tested15714 = false -function test_typed_ast_printing(f::ANY, types::ANY, must_used_vars) +function test_typed_ast_printing(Base.@nospecialize(f), Base.@nospecialize(types), must_used_vars) src, rettype = code_typed(f, types)[1] dupnames = Set() slotnames = Set() diff --git a/test/subarray.jl b/test/subarray.jl index 36d60f5f3452ec..71d79d74223ee1 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -89,10 +89,10 @@ function single_stride_dim(A::Array) end ld end -single_stride_dim(A::ANY) = single_stride_dim(copy_to_array(A)) +single_stride_dim(@nospecialize(A)) = single_stride_dim(copy_to_array(A)) # Testing equality of AbstractArrays, using several different methods to access values -function test_cartesian(A::ANY, B::ANY) +function test_cartesian(@nospecialize(A), @nospecialize(B)) isgood = true for (IA, IB) in zip(eachindex(A), eachindex(B)) if A[IA] != B[IB] @@ -108,7 +108,7 @@ function test_cartesian(A::ANY, B::ANY) end end -function test_linear(A::ANY, B::ANY) +function test_linear(@nospecialize(A), @nospecialize(B)) length(A) == length(B) || error("length mismatch") isgood = true for (iA, iB) in zip(1:length(A), 1:length(B)) @@ -129,7 +129,7 @@ end test_mixed{T}(::AbstractArray{T,1}, ::Array) = nothing test_mixed{T}(::AbstractArray{T,2}, ::Array) = nothing test_mixed(A, B::Array) = _test_mixed(A, reshape(B, size(A))) -function _test_mixed(A::ANY, B::ANY) +function _test_mixed(@nospecialize(A), @nospecialize(B)) m = size(A, 1) n = size(A, 2) isgood = true @@ -147,7 +147,7 @@ function _test_mixed(A::ANY, B::ANY) nothing end -function test_bounds(A::ANY) +function test_bounds(@nospecialize(A)) @test_throws BoundsError A[0] @test_throws BoundsError A[end+1] @test_throws BoundsError A[1, 0] @@ -183,7 +183,7 @@ function runsubarraytests(A::Array, I...) test_mixed(S, C) end -function runsubarraytests(A::ANY, I...) +function runsubarraytests(@nospecialize(A), I...) # When A was created with view, we have to check bounds, since some # of the "residual" dimensions have size 1. It's possible that we # need dedicated tests for view. diff --git a/test/subtype.jl b/test/subtype.jl index 56b0773fefa1f8..4d362f039543af 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -8,13 +8,13 @@ macro UnionAll(var, expr) end const issub = issubtype -issub_strict(x::ANY,y::ANY) = issub(x,y) && !issub(y,x) -isequal_type(x::ANY,y::ANY) = issub(x,y) && issub(y,x) -notequal_type(x::ANY,y::ANY) = !isequal_type(x, y) +issub_strict(@nospecialize(x),@nospecialize(y)) = issub(x,y) && !issub(y,x) +isequal_type(@nospecialize(x),@nospecialize(y)) = issub(x,y) && issub(y,x) +notequal_type(@nospecialize(x),@nospecialize(y)) = !isequal_type(x, y) -_type_intersect(x::ANY, y::ANY) = ccall(:jl_intersect_types, Any, (Any, Any), x, y) +_type_intersect(@nospecialize(x), @nospecialize(y)) = ccall(:jl_intersect_types, Any, (Any, Any), x, y) -intersection_env(x::ANY, y::ANY) = ccall(:jl_env_from_type_intersection, Any, (Any,Any), x, y) +intersection_env(@nospecialize(x), @nospecialize(y)) = ccall(:jl_env_from_type_intersection, Any, (Any,Any), x, y) # level 1: no varags, union, UnionAll function test_1()