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/
  • Loading branch information
JeffBezanson committed Jun 14, 2017
1 parent 493311b commit 2d6c072
Show file tree
Hide file tree
Showing 25 changed files with 137 additions and 66 deletions.
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 @@ -33,7 +33,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
2 changes: 2 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,8 @@ function LibGit2.set_remote_url(path::AbstractString, url::AbstractString; remot
LibGit2.set_remote_url(path, remote, url)
end

@deprecate fieldnames(v) fieldnames(typeof(v))

# 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
4 changes: 2 additions & 2 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1455,9 +1455,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 @@ -983,6 +983,7 @@ export
fieldoffset,
fieldname,
fieldnames,
fieldcount,
isleaftype,
oftype,
promote,
Expand Down
14 changes: 8 additions & 6 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 @@ -600,12 +600,14 @@ end
# TODO change IInf to 2 when deprecation is removed
add_tfunc(isdefined, 1, IInf, isdefined_tfunc)
add_tfunc(Core.sizeof, 1, 1, x->Int)
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 @@ -5524,8 +5526,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 @@ -30,9 +30,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 @@ -120,7 +120,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 @@ -134,16 +135,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 @@ -219,7 +213,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 @@ -321,7 +315,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 @@ -388,6 +382,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
p = ccall(:jl_argument_datatype, Ptr{Void}, (Any,), t)
if p == C_NULL
error("type does not have a definite number of fields")
end
t = unsafe_pointer_to_objref(p)::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
4 changes: 2 additions & 2 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
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
26 changes: 13 additions & 13 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ JL_CALLABLE(jl_f_sizeof)
{
JL_NARGS(sizeof, 1, 1);
jl_value_t *x = args[0];
if (jl_is_unionall(x)) {
if (jl_is_unionall(x) || jl_is_uniontype(x)) {
x = jl_unwrap_unionall(x);
if (!jl_is_datatype(x))
jl_error("argument is an abstract type; size is indeterminate");
Expand All @@ -253,10 +253,9 @@ JL_CALLABLE(jl_f_sizeof)
jl_datatype_t *dx = (jl_datatype_t*)x;
if (dx->name == jl_array_typename || dx == jl_symbol_type || dx == jl_simplevector_type ||
dx == jl_string_type)
jl_error("type does not have a canonical binary representation");
if (!(dx->name->names == jl_emptysvec && jl_datatype_size(dx) > 0)) {
// names===() and size > 0 => bitstype, size always known
if (dx->abstract || !jl_is_leaf_type(x))
jl_error("type does not have a fixed size");
if (!jl_is_primitivetype(dx)) { // size of a primitive type is always known
if (dx->layout == NULL)
jl_error("argument is an abstract type; size is indeterminate");
}
return jl_box_long(jl_datatype_size(x));
Expand All @@ -265,13 +264,13 @@ JL_CALLABLE(jl_f_sizeof)
return jl_box_long(jl_array_len(x) * ((jl_array_t*)x)->elsize);
if (jl_is_string(x))
return jl_box_long(jl_string_len(x));
if (jl_is_symbol(x))
return jl_box_long(strlen(jl_symbol_name((jl_sym_t*)x)));
if (jl_is_svec(x))
return jl_box_long((1+jl_svec_len(x))*sizeof(void*));
jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(x);
assert(jl_is_datatype(dt));
assert(!dt->abstract);
if (dt == jl_symbol_type)
jl_error("value does not have a canonical binary representation");
if (dt == jl_simplevector_type)
return jl_box_long((1+jl_svec_len(x))*sizeof(void*));
return jl_box_long(jl_datatype_size(dt));
}

Expand Down Expand Up @@ -532,9 +531,7 @@ JL_CALLABLE(jl_f_isdefined)
else {
if (!jl_is_module(args[0])) {
jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(args[0]);
if (!jl_is_datatype(vt)) {
jl_type_error("isdefined", (jl_value_t*)jl_datatype_type, args[0]);
}
assert(jl_is_datatype(vt));
size_t idx;
if (jl_is_long(args[1])) {
idx = jl_unbox_long(args[1])-1;
Expand Down Expand Up @@ -699,7 +696,10 @@ JL_CALLABLE(jl_f_nfields)
{
JL_NARGS(nfields, 1, 1);
jl_value_t *x = args[0];
if (!jl_is_datatype(x))
if (jl_is_datatype(x))
jl_depwarn("`nfields(::DataType)` is deprecated, use `fieldcount` instead",
(jl_value_t*)jl_symbol("nfields"));
else
x = jl_typeof(x);
return jl_box_long(jl_field_count(x));
}
Expand Down
2 changes: 1 addition & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3156,7 +3156,7 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
// this is issue #8798
sty != (jl_value_t*)jl_datatype_type) {
if (jl_is_leaf_type(sty) ||
(((jl_datatype_t*)sty)->name->names == jl_emptysvec && jl_datatype_size(sty) > 0)) {
(jl_field_names((jl_datatype_t*)sty) == jl_emptysvec && jl_datatype_size(sty) > 0)) {
*ret = mark_julia_type(ConstantInt::get(T_size, jl_datatype_size(sty)), false, jl_long_type, ctx);
JL_GC_POP();
return true;
Expand Down
2 changes: 1 addition & 1 deletion src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type)

JL_DLLEXPORT int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err)
{
jl_svec_t *fn = t->name->names;
jl_svec_t *fn = jl_field_names(t);
for(size_t i=0; i < jl_svec_len(fn); i++) {
if (jl_svecref(fn,i) == (jl_value_t*)fld) {
return (int)i;
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static int equiv_type(jl_datatype_t *dta, jl_datatype_t *dtb)
dta->mutabl == dtb->mutabl &&
dta->size == dtb->size &&
dta->ninitialized == dtb->ninitialized &&
jl_egal((jl_value_t*)dta->name->names, (jl_value_t*)dtb->name->names) &&
jl_egal((jl_value_t*)jl_field_names(dta), (jl_value_t*)jl_field_names(dtb)) &&
jl_nparams(dta) == jl_nparams(dtb) &&
jl_field_count(dta) == jl_field_count(dtb)))
return 0;
Expand Down
Loading

0 comments on commit 2d6c072

Please sign in to comment.