Skip to content

Commit

Permalink
clarify type reflection functions
Browse files Browse the repository at this point in the history
- deprecate `fieldnames(v)` to disambiguate whether it operates on
  types or their instances.
- similarly, separate `nfields` for instances and `fieldcount` for types
- more errors for abstract types in `sizeof`
- allow `sizeof(::Symbol)`
- use more abstract type property access in src/
- consolidate make_singleton logic to one place
  • Loading branch information
JeffBezanson committed Jul 2, 2017
1 parent b5a61db commit 2d3d6bd
Show file tree
Hide file tree
Showing 29 changed files with 193 additions and 120 deletions.
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ Deprecated or removed
Ternaries must now include some amount of whitespace, e.g. `x ? a : b` rather than
`x? a : b` ([#22523]).

* Calling `nfields` on a type to find out how many fields its instances have is deprecated.
Use `fieldcount` instead. Use `nfields` only to get the number of fields in a specific object ([#22350]).

* `fieldnames` now operates only on types. To get the names of fields in an object, use
`fieldnames(typeof(x))` ([#22350]).


Julia v0.6.0 Release Notes
==========================
Expand Down
2 changes: 1 addition & 1 deletion base/dates/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ Parse a time from a time string `t` using a `DateFormat` object `df`.
Time(t::AbstractString, df::DateFormat=ISOTimeFormat) = parse(Time, t, df)

@generated function format(io::IO, dt::TimeType, fmt::DateFormat{<:Any,T}) where T
N = nfields(T)
N = fieldcount(T)
quote
ts = fmt.tokens
loc = fmt.locale
Expand Down
2 changes: 1 addition & 1 deletion base/deepcopy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ end

function deepcopy_internal(x::ANY, stackdict::ObjectIdDict)
T = typeof(x)::DataType
nf = nfields(T)
nf = nfields(x)
(isbits(T) || nf == 0) && return x
if haskey(stackdict, x)
return stackdict[x]
Expand Down
4 changes: 4 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,10 @@ function bkfact!(A::StridedMatrix, uplo::Symbol, symmetric::Bool = issymmetric(A
return bkfact!(symmetric ? Symmetric(A, uplo) : Hermitian(A, uplo), rook)
end

@deprecate fieldnames(v) fieldnames(typeof(v))
# 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.

# END 0.7 deprecations

# BEGIN 1.0 deprecations
Expand Down
2 changes: 1 addition & 1 deletion base/distributed/messages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ let msg_cases = :(assert(false))
for i = length(msgtypes):-1:1
mti = msgtypes[i]
msg_cases = :(if idx == $i
return $(Expr(:call, QuoteNode(mti), fill(:(deserialize(s)), nfields(mti))...))
return $(Expr(:call, QuoteNode(mti), fill(:(deserialize(s)), fieldcount(mti))...))
else
$msg_cases
end)
Expand Down
2 changes: 1 addition & 1 deletion base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ function summarize(io::IO, T::DataType, binding)
" ", T, " <: ", supertype(T)
)
println(io, "```")
if !isempty(fieldnames(T))
if !T.abstract && T.name !== Tuple.name && !isempty(fieldnames(T))
println(io, "**Fields:**")
println(io, "```")
pad = maximum(length(string(f)) for f in fieldnames(T))
Expand Down
4 changes: 2 additions & 2 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1529,9 +1529,9 @@ Seek a stream to its beginning.
seekstart

"""
nfields(x::DataType) -> Int
nfields(x) -> Int
Get the number of fields of a `DataType`.
Get the number of fields in the given object.
"""
nfields

Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ export
fieldoffset,
fieldname,
fieldnames,
fieldcount,
isleaftype,
oftype,
promote,
Expand Down
16 changes: 9 additions & 7 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ function isdefined_tfunc(args...)
end
if 1 <= idx <= a1.ninitialized
return Const(true)
elseif idx <= 0 || (idx > nfields(a1) && !isvatuple(a1))
elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1))
return Const(false)
end
end
Expand All @@ -610,16 +610,18 @@ add_tfunc(Core.sizeof, 1, 1,
function (x::ANY)
isa(x, Const) && return _const_sizeof(x.val)
isa(x, Conditional) && return _const_sizeof(Bool)
isType(x) && return _const_sizeof(x.parameters[1])
isconstType(x) && return _const_sizeof(x.parameters[1])
x !== DataType && isleaftype(x) && return _const_sizeof(x)
return Int
end)
old_nfields(x::ANY) = length((isa(x,DataType) ? x : typeof(x)).types)
add_tfunc(nfields, 1, 1,
function (x::ANY)
isa(x,Const) && return Const(nfields(x.val))
isa(x,Conditional) && return Const(nfields(Bool))
isa(x,Const) && return Const(old_nfields(x.val))
isa(x,Conditional) && return Const(old_nfields(Bool))
if isType(x)
isleaftype(x.parameters[1]) && return Const(nfields(x.parameters[1]))
# TODO: remove with deprecation in builtins.c for nfields(::Type)
isleaftype(x.parameters[1]) && return Const(old_nfields(x.parameters[1]))
elseif isa(x,DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) && x !== DataType
return Const(length(x.types))
end
Expand Down Expand Up @@ -5545,8 +5547,8 @@ function is_allocation(e::ANY, sv::InferenceState)
if isa(typ, DataType) && isleaftype(typ)
nf = length(e.args) - 1
names = fieldnames(typ)
@assert(nf <= nfields(typ))
if nf < nfields(typ)
@assert(nf <= length(names))
if nf < length(names)
# some fields were left undef
# we could potentially propagate Bottom
# for pointer fields
Expand Down
6 changes: 3 additions & 3 deletions base/linalg/factorization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ end
convert(::Type{Factorization{T}}, F::Factorization{T}) where {T} = F
inv(F::Factorization{T}) where {T} = A_ldiv_B!(F, eye(T, size(F,1)))

Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, h, fieldnames(F))
Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), fieldnames(F))
Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), fieldnames(F))
Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, h, 1:nfields(F))
Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), 1:nfields(F))
Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), 1:nfields(F))

# With a real lhs and complex rhs with the same precision, we can reinterpret
# the complex rhs as a real rhs with twice the number of columns
Expand Down
2 changes: 1 addition & 1 deletion base/options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ JLOptions() = unsafe_load(cglobal(:jl_options, JLOptions))

function show(io::IO, opt::JLOptions)
print(io, "JLOptions(")
fields = fieldnames(opt)
fields = fieldnames(JLOptions)
nfields = length(fields)
for (i, f) in enumerate(fields)
v = getfield(opt, i)
Expand Down
43 changes: 31 additions & 12 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ julia> fieldname(SparseMatrixCSC, 5)
"""
fieldname(t::DataType, i::Integer) = t.name.names[i]::Symbol
fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i)
fieldname(t::Type{<:Tuple}, i::Integer) = i < 1 || i > nfields(t) ? throw(BoundsError(t, i)) : Int(i)
fieldname(t::Type{<:Tuple}, i::Integer) =
i < 1 || i > fieldcount(t) ? throw(BoundsError(t, i)) : Int(i)

"""
fieldnames(x::DataType)
Expand All @@ -139,16 +140,9 @@ julia> fieldnames(Hermitian)
:uplo
```
"""
function fieldnames(v)
t = typeof(v)
if !isa(t,DataType)
throw(ArgumentError("cannot call fieldnames() on a non-composite type"))
end
return fieldnames(t)
end
fieldnames(t::DataType) = Symbol[fieldname(t, n) for n in 1:nfields(t)]
fieldnames(t::DataType) = Symbol[fieldname(t, n) for n in 1:fieldcount(t)]
fieldnames(t::UnionAll) = fieldnames(unwrap_unionall(t))
fieldnames(t::Type{<:Tuple}) = Int[n for n in 1:nfields(t)]
fieldnames(t::Type{<:Tuple}) = Int[n for n in 1:fieldcount(t)]

"""
Base.datatype_name(t) -> Symbol
Expand Down Expand Up @@ -265,7 +259,7 @@ false
```
"""
isimmutable(x::ANY) = (@_pure_meta; (isa(x,Tuple) || !typeof(x).mutable))
isstructtype(t::DataType) = (@_pure_meta; nfields(t) != 0 || (t.size==0 && !t.abstract))
isstructtype(t::DataType) = (@_pure_meta; length(t.types) != 0 || (t.size==0 && !t.abstract))
isstructtype(x) = (@_pure_meta; false)

"""
Expand Down Expand Up @@ -371,7 +365,7 @@ The byte offset of field `i` of a type relative to the data start. For example,
use it in the following manner to summarize information about a struct:
```jldoctest
julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:nfields(T)];
julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:fieldcount(T)];
julia> structinfo(Base.Filesystem.StatStruct)
12-element Array{Tuple{UInt64,Symbol,DataType},1}:
Expand Down Expand Up @@ -440,6 +434,31 @@ end

type_alignment(x::DataType) = (@_pure_meta; ccall(:jl_get_alignment, Csize_t, (Any,), x))

"""
fieldcount(t::Type)
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)
if t isa UnionAll || t isa Union
t = ccall(:jl_argument_datatype, Any, (Any,), t)
if t === nothing
error("type does not have a definite number of fields")
end
t = t::DataType
elseif t == Union{}
return 0
end
if !(t isa DataType)
throw(TypeError(:fieldcount, "", Type, t))
end
if t.abstract || (t.name === Tuple.name && isvatuple(t))
error("type does not have a definite number of fields")
end
return length(t.types)
end

# return all instances, for types that can be enumerated

"""
Expand Down
2 changes: 1 addition & 1 deletion base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ end

function show(io::IO, ::MIME"text/plain", opt::JLOptions)
println(io, "JLOptions(")
fields = fieldnames(opt)
fields = fieldnames(JLOptions)
nfields = length(fields)
for (i, f) in enumerate(fields)
v = getfield(opt, i)
Expand Down
6 changes: 3 additions & 3 deletions base/serialize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ function serialize_any(s::AbstractSerializer, x::ANY)
return write_as_tag(s.io, tag)
end
t = typeof(x)::DataType
nf = nfields(t)
nf = nfields(x)
if nf == 0 && t.size > 0
serialize_type(s, t)
write(s.io, x)
Expand Down Expand Up @@ -718,7 +718,7 @@ function handle_deserialize(s::AbstractSerializer, b::Int32)
return deserialize_symbol(s, Int(read(s.io, Int32)::Int32))
end
t = desertag(b)
if t.mutable && nfields(t) > 0
if t.mutable && length(t.types) > 0 # manual specialization of fieldcount
slot = s.counter; s.counter += 1
push!(s.pending_refs, slot)
end
Expand Down Expand Up @@ -1046,7 +1046,7 @@ end

# default DataType deserializer
function deserialize(s::AbstractSerializer, t::DataType)
nf = nfields(t)
nf = length(t.types)
if nf == 0 && t.size > 0
# bits type
return read(s.io, t)
Expand Down
6 changes: 3 additions & 3 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function show_default(io::IO, x::ANY)
t = typeof(x)::DataType
show(io, t)
print(io, '(')
nf = nfields(t)
nf = nfields(x)
nb = sizeof(x)
if nf != 0 || nb == 0
if !show_circular(io, x)
Expand Down Expand Up @@ -1186,7 +1186,7 @@ function dump(io::IO, x::ANY, n::Int, indent)
else
print(io, T)
end
if nfields(T) > 0
if nfields(x) > 0
if n > 0
for field in (isa(x,Tuple) ? (1:length(x)) : fieldnames(T))
println(io)
Expand Down Expand Up @@ -1250,7 +1250,7 @@ function dump(io::IO, x::DataType, n::Int, indent)
if x !== Any
print(io, " <: ", supertype(x))
end
if n > 0 && !(x <: Tuple)
if n > 0 && !(x <: Tuple) && !x.abstract
tvar_io::IOContext = io
for tparam in x.parameters
# approximately recapture the list of tvar parameterization
Expand Down
9 changes: 5 additions & 4 deletions base/summarysize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct SummarySize
chargeall::Any
end

_nfields(x::ANY) = length(typeof(x).types)

"""
Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int
Expand Down Expand Up @@ -41,7 +42,7 @@ function summarysize(obj::ANY;
val = x[i]
end
else
nf = nfields(x)
nf = _nfields(x)
ft = typeof(x).types
if !isbits(ft[i]) && isdefined(x, i)
val = getfield(x, i)
Expand All @@ -65,11 +66,11 @@ end
@noinline function _summarysize(ss::SummarySize, obj::ANY)
key = pointer_from_objref(obj)
haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true)
if nfields(obj) > 0
if _nfields(obj) > 0
push!(ss.frontier_x, obj)
push!(ss.frontier_i, 1)
end
if isa(obj, UnionAll)
if isa(obj, UnionAll) || isa(obj, Union)
# black-list of items that don't have a Core.sizeof
return 2 * sizeof(Int)
end
Expand All @@ -84,7 +85,7 @@ function (ss::SummarySize)(obj::DataType)
key = pointer_from_objref(obj)
haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true)
size::Int = 7 * Core.sizeof(Int) + 6 * Core.sizeof(Int32)
size += 4 * nfields(obj) + ifelse(Sys.WORD_SIZE == 64, 4, 0)
size += 4 * _nfields(obj) + ifelse(Sys.WORD_SIZE == 64, 4, 0)
size += ss(obj.parameters)::Int
size += ss(obj.types)::Int
return size
Expand Down
2 changes: 1 addition & 1 deletion doc/src/manual/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ Stacktrace:
You may find a list of field names using the `fieldnames` function.

```jldoctest footype
julia> fieldnames(foo)
julia> fieldnames(Foo)
3-element Array{Symbol,1}:
:bar
:baz
Expand Down
1 change: 1 addition & 0 deletions doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ Base.names
Core.nfields
Base.fieldnames
Base.fieldname
Base.fieldcount
Base.datatype_module
Base.datatype_name
Base.isconst
Expand Down
Loading

0 comments on commit 2d3d6bd

Please sign in to comment.