diff --git a/NEWS.md b/NEWS.md index 1dfdb716a997f6..7c1e0889f367dc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,9 +16,6 @@ New language features * Support for Unicode 14.0.0 ([#43443]). * `try`-blocks can now optionally have an `else`-block which is executed right after the main body only if no errors were thrown. ([#42211]) -* Mutable struct fields may now be annotated as `const` to prevent changing - them after construction, providing for greater clarity and optimization - ability of these objects ([#43305]). Language changes ---------------- diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 007e7be8235cc1..f52741b5419f58 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -843,7 +843,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse if isa(typ, UnionAll) typ = unwrap_unionall(typ) end - # Could still end up here if we tried to setfield! on an immutable, which would + # Could still end up here if we tried to setfield! and immutable, which would # error at runtime, but is not illegal to have in the IR. ismutabletype(typ) || continue typ = typ::DataType @@ -868,7 +868,6 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse stmt = ir[SSAValue(def)]::Expr # == `setfield!` call field = try_compute_fieldidx_stmt(ir, stmt, typ) field === nothing && @goto skip - isconst(typ, field) && @goto skip # we discovered an attempt to mutate a const field, which must error push!(fielddefuse[field].defs, def) end # Check that the defexpr has defined values for all the fields diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 4eb4d671e0973f..622ba97aca4677 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -24,7 +24,19 @@ function find_tfunc(@nospecialize f) end end +const DATATYPE_NAME_FIELDINDEX = fieldindex(DataType, :name) +const DATATYPE_PARAMETERS_FIELDINDEX = fieldindex(DataType, :parameters) const DATATYPE_TYPES_FIELDINDEX = fieldindex(DataType, :types) +const DATATYPE_SUPER_FIELDINDEX = fieldindex(DataType, :super) +const DATATYPE_INSTANCE_FIELDINDEX = fieldindex(DataType, :instance) +const DATATYPE_HASH_FIELDINDEX = fieldindex(DataType, :hash) + +const TYPENAME_NAME_FIELDINDEX = fieldindex(Core.TypeName, :name) +const TYPENAME_MODULE_FIELDINDEX = fieldindex(Core.TypeName, :module) +const TYPENAME_NAMES_FIELDINDEX = fieldindex(Core.TypeName, :names) +const TYPENAME_WRAPPER_FIELDINDEX = fieldindex(Core.TypeName, :wrapper) +const TYPENAME_HASH_FIELDINDEX = fieldindex(Core.TypeName, :hash) +const TYPENAME_FLAGS_FIELDINDEX = fieldindex(Core.TypeName, :flags) ########## # tfuncs # @@ -293,7 +305,7 @@ function isdefined_tfunc(@nospecialize(arg1), @nospecialize(sym)) return Const(false) elseif isa(arg1, Const) arg1v = (arg1::Const).val - if !ismutable(arg1v) || isdefined(arg1v, idx) || isconst(typeof(arg1v), idx) + if !ismutable(arg1v) || isdefined(arg1v, idx) || (isa(arg1v, DataType) && is_dt_const_field(idx)) return Const(isdefined(arg1v, idx)) end elseif !isvatuple(a1) @@ -635,6 +647,23 @@ function subtype_tfunc(@nospecialize(a), @nospecialize(b)) end add_tfunc(<:, 2, 2, subtype_tfunc, 10) +is_dt_const_field(fld::Int) = ( + fld == DATATYPE_NAME_FIELDINDEX || + fld == DATATYPE_PARAMETERS_FIELDINDEX || + fld == DATATYPE_TYPES_FIELDINDEX || + fld == DATATYPE_SUPER_FIELDINDEX || + fld == DATATYPE_INSTANCE_FIELDINDEX || + fld == DATATYPE_HASH_FIELDINDEX + ) +function const_datatype_getfield_tfunc(@nospecialize(sv), fld::Int) + if fld == DATATYPE_INSTANCE_FIELDINDEX + return isdefined(sv, fld) ? Const(getfield(sv, fld)) : Union{} + elseif is_dt_const_field(fld) && isdefined(sv, fld) + return Const(getfield(sv, fld)) + end + return nothing +end + function fieldcount_noerror(@nospecialize t) if t isa UnionAll || t isa Union t = argument_datatype(t) @@ -773,27 +802,41 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) end if isa(name, Const) nv = name.val - if isa(sv, Module) - if isa(nv, Symbol) - return abstract_eval_global(sv, nv) - end + if !(isa(nv,Symbol) || isa(nv,Int)) return Bottom end - if isa(nv, Symbol) - nv = fieldindex(typeof(sv), nv, false) + if isa(sv, UnionAll) + if nv === :var || nv === 1 + return Const(sv.var) + elseif nv === :body || nv === 2 + return Const(sv.body) + end + elseif isa(sv, DataType) + idx = nv + if isa(idx, Symbol) + idx = fieldindex(DataType, idx, false) + end + if isa(idx, Int) + t = const_datatype_getfield_tfunc(sv, idx) + t === nothing || return t + end + elseif isa(sv, Core.TypeName) + fld = isa(nv, Symbol) ? fieldindex(Core.TypeName, nv, false) : nv + if (fld == TYPENAME_NAME_FIELDINDEX || + fld == TYPENAME_MODULE_FIELDINDEX || + fld == TYPENAME_WRAPPER_FIELDINDEX || + fld == TYPENAME_HASH_FIELDINDEX || + fld == TYPENAME_FLAGS_FIELDINDEX || + (fld == TYPENAME_NAMES_FIELDINDEX && isdefined(sv, fld))) + return Const(getfield(sv, fld)) + end end - if !isa(nv, Int) - return Bottom + if isa(sv, Module) && isa(nv, Symbol) + return abstract_eval_global(sv, nv) end - if isa(sv, DataType) && nv == DATATYPE_TYPES_FIELDINDEX && isdefined(sv, nv) + if (isa(sv, SimpleVector) || !ismutable(sv)) && isdefined(sv, nv) return Const(getfield(sv, nv)) end - if isconst(typeof(sv), nv) - if isdefined(sv, nv) - return Const(getfield(sv, nv)) - end - return Union{} - end end s = typeof(sv) elseif isa(s00, PartialStruct) @@ -813,11 +856,11 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return Any end s = s::DataType - if s <: Tuple && !(Int <: widenconst(name)) + if s <: Tuple && name ⊑ Symbol return Bottom end if s <: Module - if !(Symbol <: widenconst(name)) + if name ⊑ Int return Bottom end return Any @@ -878,6 +921,17 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if fld < 1 || fld > nf return Bottom end + if isconstType(s00) + sp = s00.parameters[1] + elseif isa(s00, Const) + sp = s00.val + else + sp = nothing + end + if isa(sp, DataType) + t = const_datatype_getfield_tfunc(sp, fld) + t !== nothing && return t + end R = ftypes[fld] if isempty(s.parameters) return R diff --git a/base/reflection.jl b/base/reflection.jl index f6c97a48883c94..9eaf648224fc89 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -248,34 +248,11 @@ parentmodule(t::UnionAll) = parentmodule(unwrap_unionall(t)) """ isconst(m::Module, s::Symbol) -> Bool -Determine whether a global is declared `const` in a given module `m`. +Determine whether a global is declared `const` in a given `Module`. """ isconst(m::Module, s::Symbol) = ccall(:jl_is_const, Cint, (Any, Any), m, s) != 0 -""" - isconst(t::DataType, s::Union{Int,Symbol}) -> Bool - -Determine whether a field `s` is declared `const` in a given type `t`. -""" -function isconst(@nospecialize(t::Type), s::Symbol) - t = unwrap_unionall(t) - isa(t, DataType) || return false - return isconst(t, fieldindex(t, s, false)) -end -function isconst(@nospecialize(t::Type), s::Int) - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - isa(t, DataType) || return false # uncertain - ismutabletype(t) || return true # immutable structs are always const - 1 <= s <= length(t.name.names) || return true # OOB reads are "const" since they always throw - constfields = t.name.constfields - constfields === C_NULL && return false - s -= 1 - return unsafe_load(Ptr{UInt32}(constfields), 1 + s÷32) & (1 << (s%32)) != 0 -end - - """ @locals() diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 101669f0588a52..f35e49bffcfafe 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -180,12 +180,7 @@ Base.isstructtype Base.nameof(::DataType) Base.fieldnames Base.fieldname -Core.fieldtype -Base.fieldtypes -Base.fieldcount Base.hasfield -Core.nfields -Base.isconst ``` ### Memory layout @@ -195,6 +190,9 @@ Base.sizeof(::Type) Base.isconcretetype Base.isbits Base.isbitstype +Core.fieldtype +Base.fieldtypes +Base.fieldcount Base.fieldoffset Base.datatype_alignment Base.datatype_haspadding @@ -420,6 +418,8 @@ Base.@__DIR__ Base.@__LINE__ Base.fullname Base.names +Core.nfields +Base.isconst Base.nameof(::Function) Base.functionloc(::Any, ::Any) Base.functionloc(::Method) diff --git a/src/ast.scm b/src/ast.scm index 70561897648760..a1615cc01e2fe7 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -378,7 +378,7 @@ (or (symbol? e) (decl? e))) (define (eventually-decl? e) - (or (symbol? e) (and (pair? e) (memq (car e) '(|::| atomic const)) (eventually-decl? (cadr e))))) + (or (decl? e) (and (pair? e) (eq? (car e) 'atomic) (symdecl? (cadr e))))) (define (make-decl n t) `(|::| ,n ,t)) diff --git a/src/builtins.c b/src/builtins.c index 54df354f7172b5..cae89319f40525 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -855,10 +855,6 @@ static inline size_t get_checked_fieldindex(const char *name, jl_datatype_t *st, JL_TYPECHKS(name, symbol, arg); idx = jl_field_index(st, (jl_sym_t*)arg, 1); } - if (mutabl && jl_field_isconst(st, idx)) { - jl_errorf("%s: const field .%s of type %s cannot be changed", name, - jl_symbol_name((jl_sym_t*)jl_svec_ref(jl_field_names(st), idx)), jl_symbol_name(st->name->name)); - } return idx; } @@ -1608,10 +1604,6 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb) ? dtb->name->atomicfields == NULL : (dtb->name->atomicfields != NULL && memcmp(dta->name->atomicfields, dtb->name->atomicfields, (jl_svec_len(dta->name->names) + 31) / 32 * sizeof(uint32_t)) == 0)) && - (dta->name->constfields == NULL - ? dtb->name->constfields == NULL - : (dtb->name->constfields != NULL && - memcmp(dta->name->constfields, dtb->name->constfields, (jl_svec_len(dta->name->names) + 31) / 32 * sizeof(uint32_t)) == 0)) && jl_egal((jl_value_t*)jl_field_names(dta), (jl_value_t*)jl_field_names(dtb)) && jl_nparams(dta) == jl_nparams(dtb))) return 0; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 007827ebe7b7ea..f4d7047f448d08 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2249,10 +2249,10 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st else { ptindex = emit_struct_gep(ctx, cast(lt), staddr, byte_offset + fsz); } - return emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, !jl_field_isconst(jt, idx), union_max, tbaa_unionselbyte); + return emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, jt->name->mutabl, union_max, tbaa_unionselbyte); } assert(jl_is_concrete_type(jfty)); - if (jl_field_isconst(jt, idx) && !(maybe_null && (jfty == (jl_value_t*)jl_bool_type || + if (!jt->name->mutabl && !(maybe_null && (jfty == (jl_value_t*)jl_bool_type || ((jl_datatype_t*)jfty)->layout->npointers))) { // just compute the pointer and let user load it when necessary return mark_julia_slot(addr, jfty, NULL, tbaa); @@ -3292,13 +3292,21 @@ static void emit_write_multibarrier(jl_codectx_t &ctx, Value *parent, Value *agg emit_write_barrier(ctx, parent, ptrs); } + static jl_cgval_t emit_setfield(jl_codectx_t &ctx, jl_datatype_t *sty, const jl_cgval_t &strct, size_t idx0, jl_cgval_t rhs, jl_cgval_t cmp, - bool wb, AtomicOrdering Order, AtomicOrdering FailOrder, + bool checked, bool wb, AtomicOrdering Order, AtomicOrdering FailOrder, bool needlock, bool issetfield, bool isreplacefield, bool isswapfield, bool ismodifyfield, const jl_cgval_t *modifyop, const std::string &fname) { + if (!sty->name->mutabl && checked) { + std::string msg = fname + ": immutable struct of type " + + std::string(jl_symbol_name(sty->name->name)) + + " cannot be changed"; + emit_error(ctx, msg); + return jl_cgval_t(); + } assert(strct.ispointer()); size_t byte_offset = jl_field_offset(sty, idx0); Value *addr = data_pointer(ctx, strct); @@ -3575,7 +3583,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg else need_wb = false; emit_typecheck(ctx, rhs, jl_svecref(sty->types, i), "new"); // n.b. ty argument must be concrete - emit_setfield(ctx, sty, strctinfo, i, rhs, jl_cgval_t(), need_wb, AtomicOrdering::NotAtomic, AtomicOrdering::NotAtomic, false, true, false, false, false, nullptr, ""); + emit_setfield(ctx, sty, strctinfo, i, rhs, jl_cgval_t(), false, need_wb, AtomicOrdering::NotAtomic, AtomicOrdering::NotAtomic, false, true, false, false, false, nullptr, ""); } return strctinfo; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 28c4301fd3f760..5f0cc92a3a35f6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2504,7 +2504,6 @@ static bool emit_f_opfield(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, bool isboxed = jl_field_isptr(uty, idx); bool isatomic = jl_field_isatomic(uty, idx); bool needlock = isatomic && !isboxed && jl_datatype_size(jl_field_type(uty, idx)) > MAX_ATOMIC_SIZE; - *ret = jl_cgval_t(); if (isatomic == (order == jl_memory_order_notatomic)) { emit_atomic_error(ctx, issetfield ? @@ -2518,37 +2517,25 @@ static bool emit_f_opfield(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, : "swapfield!: non-atomic field cannot be written atomically") : (isatomic ? "modifyfield!: atomic field cannot be written non-atomically" : "modifyfield!: non-atomic field cannot be written atomically")); + *ret = jl_cgval_t(); + return true; } - else if (isatomic == (fail_order == jl_memory_order_notatomic)) { + if (isatomic == (fail_order == jl_memory_order_notatomic)) { emit_atomic_error(ctx, (isatomic ? "replacefield!: atomic field cannot be accessed non-atomically" : "replacefield!: non-atomic field cannot be accessed atomically")); + *ret = jl_cgval_t(); + return true; } - else if (!uty->name->mutabl) { - std::string msg = fname + ": immutable struct of type " - + std::string(jl_symbol_name(uty->name->name)) - + " cannot be changed"; - emit_error(ctx, msg); - } - else if (jl_field_isconst(uty, idx)) { - std::string msg = fname + ": const field ." - + std::string(jl_symbol_name((jl_sym_t*)jl_svec_ref(jl_field_names(uty), idx))) - + " of type " - + std::string(jl_symbol_name(uty->name->name)) - + " cannot be changed"; - emit_error(ctx, msg); - } - else { - *ret = emit_setfield(ctx, uty, obj, idx, val, cmp, true, - (needlock || order <= jl_memory_order_notatomic) - ? (isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic) // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0 - : get_llvm_atomic_order(order), - (needlock || fail_order <= jl_memory_order_notatomic) - ? (isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic) // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0 - : get_llvm_atomic_order(fail_order), - needlock, issetfield, isreplacefield, isswapfield, ismodifyfield, - modifyop, fname); - } + *ret = emit_setfield(ctx, uty, obj, idx, val, cmp, true, true, + (needlock || order <= jl_memory_order_notatomic) + ? (isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic) // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0 + : get_llvm_atomic_order(order), + (needlock || fail_order <= jl_memory_order_notatomic) + ? (isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic) // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0 + : get_llvm_atomic_order(fail_order), + needlock, issetfield, isreplacefield, isswapfield, ismodifyfield, + modifyop, fname); return true; } } diff --git a/src/datatype.c b/src/datatype.c index e7f1ab22365b80..3825641b966232 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -79,7 +79,6 @@ JL_DLLEXPORT jl_typename_t *jl_new_typename_in(jl_sym_t *name, jl_module_t *modu tn->mt = NULL; tn->partial = NULL; tn->atomicfields = NULL; - tn->constfields = NULL; return tn; } @@ -596,7 +595,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype( t->name = NULL; if (jl_is_typename(name)) { - // This code-path is used by the Serialization module to bypass normal expectations + // This code-path is used by the Serialization module to by-pass normal expectations tn = (jl_typename_t*)name; tn->abstract = abstract; tn->mutabl = mutabl; @@ -623,7 +622,6 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype( tn->n_uninitialized = jl_svec_len(fnames) - ninitialized; uint32_t *volatile atomicfields = NULL; - uint32_t *volatile constfields = NULL; int i; JL_TRY { for (i = 0; i + 1 < jl_svec_len(fattrs); i += 2) { @@ -645,28 +643,17 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype( } atomicfields[fldn / 32] |= 1 << (fldn % 32); } - else if (attr == jl_const_sym) { - if (!mutabl) - jl_errorf("invalid field attribute const for immutable struct"); - if (constfields == NULL) { - size_t nb = (jl_svec_len(fnames) + 31) / 32 * sizeof(uint32_t); - constfields = (uint32_t*)malloc_s(nb); - memset(constfields, 0, nb); - } - constfields[fldn / 32] |= 1 << (fldn % 32); - } else { jl_errorf("invalid field attribute %s", jl_symbol_name(attr)); } } } JL_CATCH { - free(atomicfields); - free(constfields); + if (atomicfields) + free(atomicfields); jl_rethrow(); } tn->atomicfields = atomicfields; - tn->constfields = constfields; if (t->name->wrapper == NULL) { t->name->wrapper = (jl_value_t*)t; diff --git a/src/dump.c b/src/dump.c index f6e467bcce78bb..59f5112b86a88e 100644 --- a/src/dump.c +++ b/src/dump.c @@ -838,10 +838,6 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li write_int32(s->s, nb); if (nb) ios_write(s->s, (char*)tn->atomicfields, nb); - nb = tn->constfields ? (jl_svec_len(tn->names) + 31) / 32 * sizeof(uint32_t) : 0; - write_int32(s->s, nb); - if (nb) - ios_write(s->s, (char*)tn->constfields, nb); } return; } @@ -1790,11 +1786,6 @@ static jl_value_t *jl_deserialize_value_any(jl_serializer_state *s, uint8_t tag, tn->atomicfields = (uint32_t*)malloc(nfields); ios_read(s->s, (char*)tn->atomicfields, nfields); } - nfields = read_int32(s->s); - if (nfields) { - tn->constfields = (uint32_t*)malloc(nfields); - ios_read(s->s, (char*)tn->constfields, nfields); - } } else { jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(jl_get_global(m, sym)); diff --git a/src/jltypes.c b/src/jltypes.c index cfed8982cbc7c5..f14b364bda9ff3 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2003,8 +2003,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type /*jl_int32_type*/, jl_any_type /*jl_int32_type*/, jl_any_type /*jl_uint8_type*/); - const static uint32_t datatype_constfields[1] = { 0x00000097 }; // (1<<0)|(1<<1)|(1<<2)|(1<<4)|(1<<7) - jl_datatype_type->name->constfields = datatype_constfields; jl_precompute_memoized_dt(jl_datatype_type, 1); jl_typename_type->name = jl_new_typename_in(jl_symbol("TypeName"), core, 0, 1); @@ -2012,21 +2010,19 @@ void jl_init_types(void) JL_GC_DISABLED jl_typename_type->name->mt = jl_nonfunction_mt; jl_typename_type->super = jl_any_type; jl_typename_type->parameters = jl_emptysvec; - jl_typename_type->name->n_uninitialized = 13 - 2; - jl_typename_type->name->names = jl_perm_symsvec(13, "name", "module", - "names", "atomicfields", "constfields", + jl_typename_type->name->n_uninitialized = 12 - 2; + jl_typename_type->name->names = jl_perm_symsvec(12, "name", "module", + "names", "atomicfields", "wrapper", "cache", "linearcache", "mt", "partial", "hash", "n_uninitialized", "flags"); // "abstract", "mutable", "mayinlinealloc", - jl_typename_type->types = jl_svec(13, jl_symbol_type, jl_any_type /*jl_module_type*/, - jl_simplevector_type, jl_any_type/*jl_voidpointer_type*/, jl_any_type/*jl_voidpointer_type*/, + jl_typename_type->types = jl_svec(12, jl_symbol_type, jl_any_type /*jl_module_type*/, + jl_simplevector_type, jl_any_type/*jl_voidpointer_type*/, jl_type_type, jl_simplevector_type, jl_simplevector_type, jl_methtable_type, jl_any_type, jl_any_type /*jl_long_type*/, jl_any_type /*jl_int32_type*/, jl_any_type /*jl_uint8_type*/); - const static uint32_t typename_constfields[1] = { 0x00001d3f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<8)|(1<<10)|(1<<11)|(1<<12) - jl_typename_type->name->constfields = typename_constfields; jl_precompute_memoized_dt(jl_typename_type, 1); jl_methtable_type->name = jl_new_typename_in(jl_symbol("MethodTable"), core, 0, 1); @@ -2044,8 +2040,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_any_type/*module*/, jl_any_type/*any vector*/, jl_any_type/*voidpointer*/, jl_any_type/*int32*/, jl_any_type/*uint8*/, jl_any_type/*uint8*/); - const static uint32_t methtable_constfields[1] = { 0x00000040 }; // (1<<6); - jl_methtable_type->name->constfields = methtable_constfields; jl_precompute_memoized_dt(jl_methtable_type, 1); jl_symbol_type->name = jl_new_typename_in(jl_symbol("Symbol"), core, 0, 1); @@ -2222,8 +2216,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type), jl_emptysvec, 0, 1, 4); - const static uint32_t typemap_entry_constfields[1] = { 0x000003fe }; // (1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9); - jl_typemap_entry_type->name->constfields = typemap_entry_constfields; jl_function_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Function"), core, jl_any_type, jl_emptysvec); jl_builtin_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Builtin"), core, jl_function_type, jl_emptysvec); @@ -2398,19 +2390,19 @@ void jl_init_types(void) JL_GC_DISABLED "file", "line", "primary_world", - "deleted_world", // !const + "deleted_world", "sig", - "specializations", // !const - "speckeyset", // !const + "specializations", + "speckeyset", "slot_syms", "external_mt", - "source", // !const - "unspecialized", // !const - "generator", // !const - "roots", // !const - "ccallable", // !const - "invokes", // !const - "recursion_relation", // !const + "source", + "unspecialized", + "generator", + "roots", + "ccallable", + "invokes", + "recursion_relation", "nargs", "called", "nospecialize", @@ -2448,8 +2440,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_uint8_type), jl_emptysvec, 0, 1, 10); - //const static uint32_t method_constfields[1] = { 0x03fc065f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<6)|(1<<9)|(1<<10)|(1<<18)|(1<<19)|(1<<20)|(1<<21)|(1<<22)|(1<<23)|(1<<24)|(1<<25); - //jl_method_type->name->constfields = method_constfields; jl_method_instance_type = jl_new_datatype(jl_symbol("MethodInstance"), core, @@ -2474,8 +2464,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type), jl_emptysvec, 0, 1, 3); - //const static uint32_t method_instance_constfields[1] = { 0x00000007 }; // (1<<0)|(1<<1)|(1<<2); - //jl_method_instance_type->name->constfields = method_instance_constfields; jl_code_instance_type = jl_new_datatype(jl_symbol("CodeInstance"), core, @@ -2507,8 +2495,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_emptysvec, 0, 1, 1); jl_svecset(jl_code_instance_type->types, 1, jl_code_instance_type); - const static uint32_t code_instance_constfields[1] = { 0x00000001 }; // (1<<1); - jl_code_instance_type->name->constfields = code_instance_constfields; jl_const_type = jl_new_datatype(jl_symbol("Const"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(1, "val"), @@ -2624,6 +2610,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_opaque_closure_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_opaque_closure_type))->name; jl_compute_field_offsets((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_opaque_closure_type)); + jl_partial_opaque_type = jl_new_datatype(jl_symbol("PartialOpaque"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(5, "typ", "env", "isva", "parent", "source"), jl_svec(5, jl_type_type, jl_any_type, jl_bool_type, jl_method_instance_type, jl_method_type), @@ -2638,11 +2625,10 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_datatype_type->types, 8, jl_uint8_type); jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 3, jl_voidpointer_type); - jl_svecset(jl_typename_type->types, 4, jl_voidpointer_type); - jl_svecset(jl_typename_type->types, 5, jl_type_type); - jl_svecset(jl_typename_type->types, 10, jl_long_type); - jl_svecset(jl_typename_type->types, 11, jl_int32_type); - jl_svecset(jl_typename_type->types, 12, jl_uint8_type); + jl_svecset(jl_typename_type->types, 4, jl_type_type); + jl_svecset(jl_typename_type->types, 9, jl_long_type); + jl_svecset(jl_typename_type->types, 10, jl_int32_type); + jl_svecset(jl_typename_type->types, 11, jl_uint8_type); jl_svecset(jl_methtable_type->types, 4, jl_long_type); jl_svecset(jl_methtable_type->types, 6, jl_module_type); jl_svecset(jl_methtable_type->types, 7, jl_array_any_type); diff --git a/src/julia-parser.scm b/src/julia-parser.scm index ef8019722f50d0..9006fd4c2b3805 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1452,11 +1452,17 @@ (expr (if (and (pair? assgn) (eq? (car assgn) 'tuple)) (cons word (cdr assgn)) (list word assgn)))) - (if const ;; normalize `global const` and `const global` + (if const `(const ,expr) expr))) ((const) - `(const ,(parse-eq s))) + (let ((assgn (parse-eq s))) + (if (not (and (pair? assgn) + (or (eq? (car assgn) '=) + (eq? (car assgn) 'global) + (eq? (car assgn) 'local)))) + (error "expected assignment after \"const\"") + `(const ,assgn)))) ((function macro) (let* ((loc (line-number-node s)) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 1b11b487659519..e71cda3f9afee1 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -891,17 +891,17 @@ (define (struct-def-expr- name params bounds super fields0 mut) (receive - (fields defs) (separate eventually-decl? fields0) + (fields defs) (separate (lambda (x) (or (symbol? x) (eventually-decl? x))) + fields0) (let* ((attrs ()) (fields (let ((n 0)) (map (lambda (x) (set! n (+ n 1)) - (let loop ((x x)) - (if (and (pair? x) (not (decl? x))) - (begin - (set! attrs (cons (quotify (car x)) (cons n attrs))) - (loop (cadr x))) - x))) + (if (and (pair? x) (not (decl? x))) + (begin + (set! attrs (cons (quotify (car x)) (cons n attrs))) + (cadr x)) + x)) fields))) (attrs (reverse attrs)) (defs (filter (lambda (x) (not (or (effect-free? x) (eq? (car x) 'string)))) defs)) @@ -1295,9 +1295,9 @@ (if (null? f) '() (let ((x (car f))) - (cond ((or (eventually-decl? x) (linenum? x)) + (cond ((or (symdecl? x) (linenum? x)) (loop (cdr f))) - ((and (assignment? x) (eventually-decl? (cadr x))) + ((and (assignment? x) (symdecl? (cadr x))) (error (string "\"" (deparse x) "\" inside type definition is reserved"))) (else '()))))) (expand-forms @@ -1385,7 +1385,7 @@ (expand-forms (expand-decls (car arg) (cdr arg) #t))) ((= |::|) (expand-forms (expand-decls 'const (cdr e) #f))) - (else (error "expected assignment after \"const\"")))))) + (else e))))) (define (expand-atomic-decl e) (error "unimplemented or unsupported atomic declaration")) @@ -1708,9 +1708,9 @@ ,@(if outer `((require-existing-local ,lhs)) '()) (if (call (top not_int) (call (core ===) ,next (null))) (_do_while - (block ,body - (= ,next (call (top iterate) ,coll ,state))) - (call (top not_int) (call (core ===) ,next (null)))))))))))) + (block ,body + (= ,next (call (top iterate) ,coll ,state))) + (call (top not_int) (call (core ===) ,next (null)))))))))))) ;; wrap `expr` in a function appropriate for consuming values from given ranges (define (func-for-generator-ranges expr range-exprs flat outervars) diff --git a/src/julia.h b/src/julia.h index f626f3026e67e9..5663a6c310052d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -415,7 +415,7 @@ typedef struct { struct _jl_module_t *module; jl_svec_t *names; // field names const uint32_t *atomicfields; // if any fields are atomic, we record them here - const uint32_t *constfields; // if any fields are const, we record them here + //const uint32_t *constfields; // if any fields are const, we record them here // `wrapper` is either the only instantiation of the type (if no parameters) // or a UnionAll accepting parameters to make an instantiation. jl_value_t *wrapper; @@ -1114,6 +1114,7 @@ static inline uint32_t jl_ptr_offset(jl_datatype_t *st, int i) JL_NOTSAFEPOINT static inline int jl_field_isatomic(jl_datatype_t *st, int i) JL_NOTSAFEPOINT { + // if (!st->mutable) return 0; // TODO: is this fast-path helpful? const uint32_t *atomicfields = st->name->atomicfields; if (atomicfields != NULL) { if (atomicfields[i / 32] & (1 << (i % 32))) @@ -1122,20 +1123,6 @@ static inline int jl_field_isatomic(jl_datatype_t *st, int i) JL_NOTSAFEPOINT return 0; } -static inline int jl_field_isconst(jl_datatype_t *st, int i) JL_NOTSAFEPOINT -{ - jl_typename_t *tn = st->name; - if (!tn->mutabl) - return 1; - const uint32_t *constfields = tn->constfields; - if (constfields != NULL) { - if (constfields[i / 32] & (1 << (i % 32))) - return 1; - } - return 0; -} - - static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEPOINT { return l->nfields == 0 && l->npointers > 0; diff --git a/src/staticdata.c b/src/staticdata.c index 634728991a3586..223d7b8e634278 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1064,16 +1064,6 @@ static void jl_write_values(jl_serializer_state *s) arraylist_push(&s->relocs_list, (void*)(((uintptr_t)ConstDataRef << RELOC_TAG_OFFSET) + layout)); // relocation target ios_write(s->const_data, (char*)tn->atomicfields, nb); } - if (tn->constfields != NULL) { - size_t nb = (jl_svec_len(tn->names) + 31) / 32 * sizeof(uint32_t); - uintptr_t layout = LLT_ALIGN(ios_pos(s->const_data), sizeof(void*)); - write_padding(s->const_data, layout - ios_pos(s->const_data)); // realign stream - newtn->constfields = NULL; // relocation offset - layout /= sizeof(void*); - arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_typename_t, constfields))); // relocation location - arraylist_push(&s->relocs_list, (void*)(((uintptr_t)ConstDataRef << RELOC_TAG_OFFSET) + layout)); // relocation target - ios_write(s->const_data, (char*)tn->constfields, nb); - } } else if (((jl_datatype_t*)(jl_typeof(v)))->name == jl_idtable_typename) { // will need to rehash this, later (after types are fully constructed) diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 7df53f216716f6..acf738bd5a32f5 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1273,11 +1273,10 @@ function deserialize_typename(s::AbstractSerializer, number) else # reuse the same name for the type, if possible, for nicer debugging tn_name = isdefined(__deserialized_types__, name) ? gensym() : name - tn = ccall(:jl_new_typename_in, Any, (Any, Any, Cint, Cint), + tn = ccall(:jl_new_typename_in, Ref{Core.TypeName}, (Any, Any, Cint, Cint), tn_name, __deserialized_types__, false, false) makenew = true end - tn = tn::Core.TypeName remember_object(s, tn, number) deserialize_cycle(s, tn) @@ -1292,22 +1291,19 @@ function deserialize_typename(s::AbstractSerializer, number) ninitialized = deserialize(s)::Int32 if makenew + Core.setfield!(tn, :names, names) # TODO: there's an unhanded cycle in the dependency graph at this point: # while deserializing super and/or types, we may have encountered # tn.wrapper and throw UndefRefException before we get to this point ndt = ccall(:jl_new_datatype, Any, (Any, Any, Any, Any, Any, Any, Any, Cint, Cint, Cint), tn, tn.module, super, parameters, names, types, attrs, abstr, mutabl, ninitialized) - @assert tn == ndt.name + tn.wrapper = ndt.name.wrapper ccall(:jl_set_const, Cvoid, (Any, Any, Any), tn.module, tn.name, tn.wrapper) ty = tn.wrapper - if has_instance - ty = ty::DataType - if !isdefined(ty, :instance) - singleton = ccall(:jl_new_struct, Any, (Any, Any...), ty) - # use setfield! directly to avoid `fieldtype` lowering expecting to see a Singleton object already on ty - ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), ty, Base.fieldindex(DataType, :instance)-1, singleton) - end + if has_instance && !isdefined(ty, :instance) + # use setfield! directly to avoid `fieldtype` lowering expecting to see a Singleton object already on ty + Core.setfield!(ty, :instance, ccall(:jl_new_struct, Any, (Any, Any...), ty)) end end @@ -1317,16 +1313,15 @@ function deserialize_typename(s::AbstractSerializer, number) defs = deserialize(s) maxa = deserialize(s)::Int if makenew - mt = ccall(:jl_new_method_table, Any, (Any, Any), name, tn.module) + tn.mt = ccall(:jl_new_method_table, Any, (Any, Any), name, tn.module) if !isempty(parameters) - mt.offs = 0 + tn.mt.offs = 0 end - mt.name = mtname - mt.max_args = maxa - ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), tn, Base.fieldindex(Core.TypeName, :mt)-1, mt) + tn.mt.name = mtname + tn.mt.max_args = maxa for def in defs if isdefined(def, :sig) - ccall(:jl_method_table_insert, Cvoid, (Any, Any, Ptr{Cvoid}), mt, def, C_NULL) + ccall(:jl_method_table_insert, Cvoid, (Any, Any, Ptr{Cvoid}), tn.mt, def, C_NULL) end end end @@ -1338,10 +1333,9 @@ function deserialize_typename(s::AbstractSerializer, number) end end elseif makenew - mt = Symbol.name.mt - ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), tn, Base.fieldindex(Core.TypeName, :mt)-1, mt) + tn.mt = Symbol.name.mt end - return tn + return tn::Core.TypeName end function deserialize_datatype(s::AbstractSerializer, full::Bool) diff --git a/test/core.jl b/test/core.jl index 6c36827d42afb4..3727d3ff8f0695 100644 --- a/test/core.jl +++ b/test/core.jl @@ -9,90 +9,6 @@ const Bottom = Union{} # For curmod_* include("testenv.jl") -## tests that `const` field declarations - -# sanity tests that our built-in types are marked correctly for const fields -for (T, c) in ( - (Core.CodeInfo, []), - (Core.CodeInstance, [:def]), - (Core.Method, [#=:name, :module, :file, :line, :primary_world, :sig, :slot_syms, :external_mt, :nargs, :called, :nospecialize, :nkw, :isva, :pure, :is_for_opaque_closure, :constprop=#]), - (Core.MethodInstance, [#=:def, :specTypes, :sparam_vals]=#]), - (Core.MethodTable, [:module]), - (Core.TypeMapEntry, [:sig, :simplesig, :guardsigs, :min_world, :max_world, :func, :isleafsig, :issimplesig, :va]), - (Core.TypeMapLevel, []), - (Core.TypeName, [:name, :module, :names, :atomicfields, :constfields, :wrapper, :mt, :hash, :n_uninitialized, :flags]), - (DataType, [:name, :super, :parameters, :instance, :hash]), - ) - @test Set((fieldname(T, i) for i in 1:fieldcount(T) if isconst(T, i))) == Set(c) -end - -@test_throws(ErrorException("setfield!: const field .name of type DataType cannot be changed"), - setfield!(Int, :name, Int.name)) -@test_throws(ErrorException("setfield!: const field .name of type DataType cannot be changed"), - (Base.Experimental.@force_compile; setfield!(Int, :name, Int.name))) - -@test_throws(ErrorException("invalid field attribute const for immutable struct"), - @eval struct ABCDconst - const abcd - end) -mutable struct ABCDconst - const a - const b::Int - c - const d::Union{Int,Nothing} -end -@test_throws(ErrorException("invalid redefinition of constant ABCDconst"), - mutable struct ABCDconst - const a - const b::Int - c - d::Union{Int,Nothing} - end) -@test_throws(ErrorException("invalid redefinition of constant ABCDconst"), - mutable struct ABCDconst - a - b::Int - c - d::Union{Int,Nothing} - end) -let abcd = ABCDconst(1, 2, 3, 4) - @test (1, 2, 3, 4) === (abcd.a, abcd.b, abcd.c, abcd.d) - @test_throws(ErrorException("setfield!: const field .a of type ABCDconst cannot be changed"), - abcd.a = 0) - @test_throws(ErrorException("replacefield!: const field .a of type ABCDconst cannot be changed"), - replacefield!(abcd, :a, 1, 0)) - @test_throws(ErrorException("modifyfield!: const field .a of type ABCDconst cannot be changed"), - modifyfield!(abcd, :a, +, 1)) - @test_throws(ErrorException("swapfield!: const field .a of type ABCDconst cannot be changed"), - swapfield!(abcd, :a, 0)) - @test_throws(ErrorException("setfield!: const field .b of type ABCDconst cannot be changed"), - abcd.b = 0) - abcd.c = "not constant" - @test_throws(ErrorException("setfield!: const field .d of type ABCDconst cannot be changed"), - abcd.d = nothing) - @test (1, 2, "not constant", 4) === (abcd.a, abcd.b, abcd.c, abcd.d) -end -# repeat with the compiler -let abcd = ABCDconst(1, 2, 3, 4) - Base.Experimental.@force_compile - @test (1, 2, 3, 4) === (abcd.a, abcd.b, abcd.c, abcd.d) - @test_throws(ErrorException("setfield!: const field .a of type ABCDconst cannot be changed"), - abcd.a = 0) - @test_throws(ErrorException("replacefield!: const field .a of type ABCDconst cannot be changed"), - replacefield!(abcd, :a, 1, 0)) - @test_throws(ErrorException("modifyfield!: const field .a of type ABCDconst cannot be changed"), - modifyfield!(abcd, :a, +, 1)) - @test_throws(ErrorException("swapfield!: const field .a of type ABCDconst cannot be changed"), - swapfield!(abcd, :a, 0)) - @test_throws(ErrorException("setfield!: const field .b of type ABCDconst cannot be changed"), - abcd.b = 0) - abcd.c = "not constant" - @test_throws(ErrorException("setfield!: const field .d of type ABCDconst cannot be changed"), - abcd.d = nothing) - @test (1, 2, "not constant", 4) === (abcd.a, abcd.b, abcd.c, abcd.d) -end - - f47(x::Vector{Vector{T}}) where {T} = 0 @test_throws MethodError f47(Vector{Vector}()) @test f47(Vector{Vector{Int}}()) == 0