diff --git a/NEWS.md b/NEWS.md index 121a07446baeb..e58df199b7c69 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 ========================== diff --git a/base/dates/io.jl b/base/dates/io.jl index f7ce2480feffc..f588cfd0d4137 100644 --- a/base/dates/io.jl +++ b/base/dates/io.jl @@ -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 diff --git a/base/deepcopy.jl b/base/deepcopy.jl index 4802c3980e859..d7d61ab813e51 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -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] diff --git a/base/deprecated.jl b/base/deprecated.jl index 828b3ce5c7538..4d4bf760f8fc3 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -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 diff --git a/base/distributed/messages.jl b/base/distributed/messages.jl index 17a447a154d1f..7d9afbdec2962 100644 --- a/base/distributed/messages.jl +++ b/base/distributed/messages.jl @@ -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) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 4ea4867bda60a..42c0e22390e22 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -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)) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 52eb18b4c334e..6eedcb78a68f9 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -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 diff --git a/base/exports.jl b/base/exports.jl index 34e266a0ee002..78a7409ff20ac 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -946,6 +946,7 @@ export fieldoffset, fieldname, fieldnames, + fieldcount, isleaftype, oftype, promote, diff --git a/base/inference.jl b/base/inference.jl index c2fcdd04f04b6..610bfcdf37887 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -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 @@ -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 @@ -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 diff --git a/base/linalg/factorization.jl b/base/linalg/factorization.jl index a1aa8e0d6ab68..e3b10d258857c 100644 --- a/base/linalg/factorization.jl +++ b/base/linalg/factorization.jl @@ -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 diff --git a/base/options.jl b/base/options.jl index 660db20b928c3..27b7a512fa5b5 100644 --- a/base/options.jl +++ b/base/options.jl @@ -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) diff --git a/base/reflection.jl b/base/reflection.jl index 83b6924e00c88..8f0ae46e9d80b 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -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) @@ -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 @@ -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) """ @@ -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}: @@ -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 """ diff --git a/base/replutil.jl b/base/replutil.jl index 6068c159b9432..519eb9066fd93 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -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) diff --git a/base/serialize.jl b/base/serialize.jl index b8e2387e98d00..33400aa791987 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -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) @@ -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 @@ -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) diff --git a/base/show.jl b/base/show.jl index b7b1a77032257..0209cc92c9fad 100644 --- a/base/show.jl +++ b/base/show.jl @@ -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) @@ -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) @@ -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 diff --git a/base/summarysize.jl b/base/summarysize.jl index b7e320529527a..133e6a5edfbb2 100644 --- a/base/summarysize.jl +++ b/base/summarysize.jl @@ -8,6 +8,7 @@ struct SummarySize chargeall::Any end +_nfields(x::ANY) = length(typeof(x).types) """ Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int @@ -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) @@ -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 @@ -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 diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 281070a7e8d77..e7f428c191b4b 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -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 diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index d17eb625344be..2b1c658c00a81 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -257,6 +257,7 @@ Base.names Core.nfields Base.fieldnames Base.fieldname +Base.fieldcount Base.datatype_module Base.datatype_name Base.isconst diff --git a/src/builtins.c b/src/builtins.c index 91a75ab984321..3a0014144a107 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -244,33 +244,30 @@ 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"); } if (jl_is_datatype(x)) { jl_datatype_t *dx = (jl_datatype_t*)x; - if (dx->layout && jl_is_layout_opaque(dx->layout)) - 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("argument is an abstract type; size is indeterminate"); - } + if (dx->layout == NULL) + jl_error("argument is an abstract type; size is indeterminate"); + if (jl_is_layout_opaque(dx->layout)) + jl_error("type does not have a fixed size"); return jl_box_long(jl_datatype_size(x)); } if (jl_is_array(x)) 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)); } @@ -531,9 +528,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; @@ -698,7 +693,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)); } diff --git a/src/codegen.cpp b/src/codegen.cpp index ed71940d5a5b3..11b80bb761006 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2941,7 +2941,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; diff --git a/src/datatype.c b/src/datatype.c index f91d9331dff66..e97a26ad53bf1 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -216,6 +216,20 @@ unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) return alignment; } +STATIC_INLINE int jl_is_datatype_make_singleton(jl_datatype_t *d) +{ + return (!d->abstract && jl_datatype_size(d) == 0 && d != jl_sym_type && d->name != jl_array_typename && + d->uid != 0 && (d->types == jl_emptysvec || !d->mutabl)); +} + +STATIC_INLINE void jl_allocate_singleton_instance(jl_datatype_t *st) +{ + if (jl_is_datatype_make_singleton(st)) { + st->instance = jl_gc_alloc(jl_get_ptls_states(), 0, st); + jl_gc_wb(st, st->instance); + } +} + void jl_compute_field_offsets(jl_datatype_t *st) { size_t sz = 0, alignm = 1; @@ -232,6 +246,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) w->layout) { st->layout = w->layout; st->size = w->size; + jl_allocate_singleton_instance(st); return; } } @@ -252,6 +267,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) // reuse the same layout for all singletons static const jl_datatype_layout_t singleton_layout = {0, 1, 0, 0, 0}; st->layout = &singleton_layout; + jl_allocate_singleton_instance(st); } return; } @@ -327,6 +343,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) haspadding = 1; st->layout = jl_get_layout(nfields, alignm, haspadding, desc); if (descsz >= jl_page_size) free(desc); + jl_allocate_singleton_instance(st); return; throw_ovf: if (descsz >= jl_page_size) free(desc); @@ -420,6 +437,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * alignm = MAX_ALIGN; bt->size = nbytes; bt->layout = jl_get_layout(0, alignm, 0, NULL); + bt->instance = NULL; return bt; } @@ -688,7 +706,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; diff --git a/src/gf.c b/src/gf.c index 1247383b24c47..04fbae6bca875 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2120,8 +2120,8 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty) { - jl_datatype_t *dt = jl_argument_datatype(ty); - if (dt == NULL) + jl_datatype_t *dt = (jl_datatype_t*)jl_argument_datatype(ty); + if ((jl_value_t*)dt == jl_nothing) jl_error("cannot get keyword sorter for abstract type"); jl_typename_t *tn = dt->name; jl_methtable_t *mt = tn->mt; diff --git a/src/interpreter.c b/src/interpreter.c index 55ac7b7b819a3..34066a5b781e5 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -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; @@ -475,10 +475,6 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) jl_rethrow(); } jl_compute_field_offsets(dt); - if (para == (jl_value_t*)jl_emptysvec && jl_is_datatype_make_singleton(dt)) { - dt->instance = jl_gc_alloc(ptls, 0, dt); - jl_gc_wb(dt, dt->instance); - } b->value = temp; if (temp == NULL || !equiv_type(dt, (jl_datatype_t*)jl_unwrap_unionall(temp))) { diff --git a/src/jltypes.c b/src/jltypes.c index b74b39da06331..a077a939bc527 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1018,7 +1018,6 @@ static jl_value_t *normalize_vararg(jl_value_t *va) static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, int cacheable, jl_typestack_t *stack, jl_typeenv_t *env) { - jl_ptls_t ptls = jl_get_ptls_states(); jl_typestack_t top; jl_typename_t *tn = dt->name; int istuple = (tn == jl_tuple_typename); @@ -1166,16 +1165,6 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_gc_wb(ndt, ndt->super); } jl_svec_t *ftypes = dt->types; - if (!istuple && ndt->name->names == jl_emptysvec) { - assert(ftypes == NULL || ftypes == jl_emptysvec); - ndt->size = dt->size; - ndt->layout = dt->layout; - ndt->types = jl_emptysvec; - if (jl_is_datatype_make_singleton(ndt)) { - ndt->instance = jl_gc_alloc(ptls, 0, ndt); - jl_gc_wb(ndt, ndt->instance); - } - } if (ftypes == NULL || dt->super == NULL) { // in the process of creating this type definition: // need to instantiate the super and types fields later @@ -1183,25 +1172,22 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value arraylist_push(&partial_inst, ndt); } else { - if (ftypes != jl_emptysvec) { - assert(!ndt->abstract); - if (!istuple) { - // recursively instantiate the types of the fields - ndt->types = inst_all(ftypes, env, stack, 1); - jl_gc_wb(ndt, ndt->types); - } - if (cacheable) { - jl_compute_field_offsets(ndt); - if (jl_is_datatype_make_singleton(ndt)) { - ndt->instance = jl_gc_alloc(ptls, 0, ndt); - jl_gc_wb(ndt, ndt->instance); - } - } - } - else { - assert(ndt->name->names == jl_emptysvec); + assert(ftypes != jl_emptysvec || jl_field_names(ndt) == jl_emptysvec); + assert(ftypes == jl_emptysvec || !ndt->abstract); + if (!istuple) { + // recursively instantiate the types of the fields + ndt->types = inst_all(ftypes, env, stack, 1); + jl_gc_wb(ndt, ndt->types); } } + if (jl_is_primitivetype(dt)) { + ndt->size = dt->size; + ndt->layout = dt->layout; + } + else if (cacheable && ndt->types != NULL && !ndt->abstract) { + jl_compute_field_offsets(ndt); + } + if (istuple) ndt->ninitialized = ntp - isvatuple; else @@ -1487,7 +1473,6 @@ jl_value_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! { - jl_ptls_t ptls = jl_get_ptls_states(); inside_typedef = 0; assert(jl_is_datatype(t)); jl_typestack_t top; @@ -1515,7 +1500,7 @@ void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! jl_gc_wb(ndt, ndt->super); } - if (t->name->names != jl_emptysvec) { + if (t->types != jl_emptysvec) { for (j = 0; j < partial_inst.len; j++) { jl_datatype_t *ndt = (jl_datatype_t*)partial_inst.items[j]; for (i = 0; i < n; i++) @@ -1530,15 +1515,11 @@ void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! } if (ndt->uid) { // cacheable jl_compute_field_offsets(ndt); - if (jl_is_datatype_make_singleton(ndt)) { - ndt->instance = jl_gc_alloc(ptls, 0, ndt); - jl_gc_wb(ndt, ndt->instance); - } } } } else { - assert(t->types == jl_emptysvec); + assert(jl_field_names(t) == jl_emptysvec); } partial_inst.len = 0; } @@ -1741,6 +1722,7 @@ void jl_init_types(void) jl_anytuple_type->parameters = jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type, (jl_value_t*)NULL)); jl_anytuple_type->types = jl_anytuple_type->parameters; jl_anytuple_type->layout = NULL; + jl_anytuple_type->instance = NULL; jl_anytuple_type->hasfreetypevars = 0; jl_anytuple_type->isleaftype = 0; @@ -2054,6 +2036,7 @@ void jl_init_types(void) jl_abstractstring_type = jl_new_abstracttype((jl_value_t*)jl_symbol("AbstractString"), core, jl_any_type, jl_emptysvec); jl_string_type = jl_new_datatype(jl_symbol("String"), core, jl_abstractstring_type, jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 1, 0); + jl_string_type->instance = NULL; jl_compute_field_offsets(jl_string_type); // complete builtin type metadata diff --git a/src/julia.h b/src/julia.h index 0b433057e5e01..f204f37fb59e2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -778,7 +778,14 @@ STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) #define jl_gf_name(f) (jl_gf_mtable(f)->name) // struct type info -#define jl_field_name(st,i) (jl_sym_t*)jl_svecref(((jl_datatype_t*)st)->name->names, (i)) +STATIC_INLINE jl_svec_t *jl_field_names(jl_datatype_t *st) +{ + return st->name->names; +} +STATIC_INLINE jl_sym_t *jl_field_name(jl_datatype_t *st, size_t i) +{ + return (jl_sym_t*)jl_svecref(jl_field_names(st), i); +} #define jl_field_type(st,i) jl_svecref(((jl_datatype_t*)st)->types, (i)) #define jl_field_count(st) jl_svec_len(((jl_datatype_t*)st)->types) #define jl_datatype_size(t) (((jl_datatype_t*)t)->size) @@ -908,9 +915,8 @@ STATIC_INLINE int jl_is_primitivetype(void *v) STATIC_INLINE int jl_is_structtype(void *v) { return (jl_is_datatype(v) && - (jl_field_count(v) > 0 || - jl_datatype_size(v) == 0) && - !((jl_datatype_t*)(v))->abstract); + !((jl_datatype_t*)(v))->abstract && + !jl_is_primitivetype(v)); } STATIC_INLINE int jl_isbits(void *t) // corresponding to isbits() in julia @@ -925,12 +931,6 @@ STATIC_INLINE int jl_is_datatype_singleton(jl_datatype_t *d) return (d->instance != NULL); } -STATIC_INLINE int jl_is_datatype_make_singleton(jl_datatype_t *d) -{ - return (!d->abstract && jl_datatype_size(d) == 0 && d != jl_sym_type && d->name != jl_array_typename && - d->uid != 0 && (d->name->names == jl_emptysvec || !d->mutabl)); -} - STATIC_INLINE int jl_is_abstracttype(void *v) { return (jl_is_datatype(v) && ((jl_datatype_t*)(v))->abstract); diff --git a/src/julia_internal.h b/src/julia_internal.h index c9ee0d4dc1822..59003a24a43f6 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -510,7 +510,7 @@ JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int size_t world, size_t *min_valid, size_t *max_valid); JL_DLLEXPORT jl_datatype_t *jl_first_argument_datatype(jl_value_t *argtypes); -jl_datatype_t *jl_argument_datatype(jl_value_t *argt); +JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt); jl_value_t *jl_nth_slot_type(jl_value_t *sig, size_t i); void jl_compute_field_offsets(jl_datatype_t *st); diff --git a/src/method.c b/src/method.c index 630d3d60d2072..b5942c023e86e 100644 --- a/src/method.c +++ b/src/method.c @@ -617,10 +617,13 @@ JL_DLLEXPORT jl_datatype_t *jl_first_argument_datatype(jl_value_t *argtypes) return first_arg_datatype(argtypes, 0); } -// get DataType implied by a single given type -jl_datatype_t *jl_argument_datatype(jl_value_t *argt) +// get DataType implied by a single given type, or `nothing` +JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt) { - return first_arg_datatype(argt, 1); + jl_datatype_t *dt = first_arg_datatype(argt, 1); + if (dt == NULL) + return jl_nothing; + return (jl_value_t*)dt; } extern tracer_cb jl_newmeth_tracer; diff --git a/src/rtutils.c b/src/rtutils.c index f6a1e4cb68083..aa52b084991d8 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -559,7 +559,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL; int globfunc = 0; if (globname && !strchr(jl_symbol_name(globname), '#') && - !strchr(jl_symbol_name(globname), '@') && + !strchr(jl_symbol_name(globname), '@') && dv->name->module && jl_binding_resolved_p(dv->name->module, globname)) { jl_binding_t *b = jl_get_binding(dv->name->module, globname); if (b && jl_typeof(b->value) == v) @@ -868,7 +868,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt i = 1; for (; i < tlen; i++) { if (!istuple) { - n += jl_printf(out, "%s", jl_symbol_name((jl_sym_t*)jl_svecref(vt->name->names, i))); + n += jl_printf(out, "%s", jl_symbol_name(jl_field_name(vt, i))); n += jl_printf(out, "="); } size_t offs = jl_field_offset(vt, i); diff --git a/test/reflection.jl b/test/reflection.jl index 5a85bb31c729f..77a4674b03300 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -293,8 +293,8 @@ mutable struct TLayout z::Int32 end tlayout = TLayout(5,7,11) -@test fieldnames(tlayout) == fieldnames(TLayout) == [:x, :y, :z] -@test [(fieldoffset(TLayout,i), fieldname(TLayout,i), fieldtype(TLayout,i)) for i = 1:nfields(TLayout)] == +@test fieldnames(TLayout) == [:x, :y, :z] +@test [(fieldoffset(TLayout,i), fieldname(TLayout,i), fieldtype(TLayout,i)) for i = 1:fieldcount(TLayout)] == [(0, :x, Int8), (2, :y, Int16), (4, :z, Int32)] @test_throws BoundsError fieldtype(TLayout, 0) @test_throws BoundsError fieldname(TLayout, 0) @@ -307,7 +307,7 @@ tlayout = TLayout(5,7,11) @test fieldtype(Tuple{Vararg{Int8}}, 10) === Int8 @test_throws BoundsError fieldtype(Tuple{Vararg{Int8}}, 0) -@test fieldnames((1,2,3)) == fieldnames(NTuple{3, Int}) == [fieldname(NTuple{3, Int}, i) for i = 1:3] == [1, 2, 3] +@test fieldnames(NTuple{3, Int}) == [fieldname(NTuple{3, Int}, i) for i = 1:3] == [1, 2, 3] @test_throws BoundsError fieldname(NTuple{3, Int}, 0) @test_throws BoundsError fieldname(NTuple{3, Int}, 4) @@ -649,3 +649,44 @@ struct B20086{T,N} <: A20086{T,N} end @test subtypes(A20086{Int}) == [B20086{Int}] @test subtypes(A20086{T,3} where T) == [B20086{T,3} where T] @test subtypes(A20086{Int,3}) == [B20086{Int,3}] + +# sizeof and nfields +@test sizeof(Int16) == 2 +@test sizeof(Complex128) == 16 +primitive type ParameterizedByte__{A,B} 8 end +@test sizeof(ParameterizedByte__) == 1 +@test sizeof(nothing) == 0 +@test sizeof(()) == 0 +struct TypeWithIrrelevantParameter{T} + x::Int32 +end +@test sizeof(TypeWithIrrelevantParameter) == sizeof(Int32) +@test sizeof(TypeWithIrrelevantParameter{Int8}) == sizeof(Int32) +@test sizeof(:abc) == 3 +@test sizeof(Symbol("")) == 0 +@test_throws(ErrorException("argument is an abstract type; size is indeterminate"), + sizeof(Real)) +@test_throws ErrorException sizeof(Union{Complex64,Complex128}) +@test_throws ErrorException sizeof(Union{Int8,UInt8}) +@test_throws ErrorException sizeof(AbstractArray) +@test_throws ErrorException sizeof(Tuple) +@test_throws ErrorException sizeof(Tuple{Any,Any}) +@test_throws ErrorException sizeof(String) +@test_throws ErrorException sizeof(Vector{Int}) +@test_throws ErrorException sizeof(Symbol) +@test_throws ErrorException sizeof(SimpleVector) + +@test nfields((1,2)) == 2 +@test nfields(()) == 0 +@test nfields(nothing) == fieldcount(Void) == 0 +@test nfields(1) == 0 +@test fieldcount(Union{}) == 0 +@test fieldcount(Tuple{Any,Any,T} where T) == 3 +@test fieldcount(Complex) == fieldcount(Complex64) == 2 +@test fieldcount(Union{Complex64,Complex128}) == 2 +@test fieldcount(Int) == 0 +@test_throws(ErrorException("type does not have a definite number of fields"), + fieldcount(Union{Complex,Pair})) +@test_throws ErrorException fieldcount(Real) +@test_throws ErrorException fieldcount(AbstractArray) +@test_throws ErrorException fieldcount(Tuple{Any,Vararg{Any}})