diff --git a/NEWS.md b/NEWS.md index 9ecd5567e6fcb..cb91056fca7e5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,8 @@ New language features * Binary `~` now parses as a vararg macro call to `@~`. For example `x~y~z` => `@~ x y z` ([#4882]). + * Structure fields can now be accessed by index ([#4806]). + New library functions --------------------- @@ -140,6 +142,7 @@ Deprecated or removed [#2345]: https://github.com/JuliaLang/julia/issues/2345 [#5330]: https://github.com/JuliaLang/julia/issues/5330 [#4882]: https://github.com/JuliaLang/julia/issues/4882 +[#4806]: https://github.com/JuliaLang/julia/issues/4806 Julia v0.2.0 Release Notes ========================== diff --git a/base/deepcopy.jl b/base/deepcopy.jl index ba8a4e5c65307..d96c36637c7a7 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -32,9 +32,9 @@ function _deepcopy_t(x, T::DataType, stackdict::ObjectIdDict) end ret = ccall(:jl_new_struct_uninit, Any, (Any,), T) stackdict[x] = ret - for f in T.names - if isdefined(x,f) - ret.(f) = deepcopy_internal(x.(f), stackdict) + for i in 1:length(T.names) + if isdefined(x,i) + ret.(i) = deepcopy_internal(x.(i), stackdict) end end return ret diff --git a/base/inference.jl b/base/inference.jl index 7c1463eac7606..9e357baa3877c 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -312,6 +312,15 @@ const getfield_tfunc = function (A, s, name) end end return None + elseif isa(A[2],Int) + if isa(A[1],Module) || s === Module + return None + end + i::Int = A[2] + if i < 1 || i > length(s.names) + return None + end + return s.types[i] else return reduce(tmerge, None, s.types)#Union(s.types...) end diff --git a/base/serialize.jl b/base/serialize.jl index 3f3173d4fc39d..cd7cdf5fe1cf7 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -289,9 +289,9 @@ function serialize(s, x) write(s, x) else serialize(s, length(t.names)) - for n in t.names - if isdefined(x, n) - serialize(s, getfield(x, n)) + for i in 1:length(t.names) + if isdefined(x, i) + serialize(s, getfield(x, i)) else writetag(s, UndefRefTag) end @@ -502,10 +502,10 @@ function deserialize(s, t::DataType) end else x = ccall(:jl_new_struct_uninit, Any, (Any,), t) - for n in t.names + for i in 1:length(t.names) tag = int32(read(s, Uint8)) if tag==0 || !is(deser_tag[tag], UndefRefTag) - setfield(x, n, handle_deserialize(s, tag)) + setfield(x, i, handle_deserialize(s, tag)) end end return x diff --git a/base/show.jl b/base/show.jl index d8b0aadd35187..a3818201fc0af 100644 --- a/base/show.jl +++ b/base/show.jl @@ -16,7 +16,7 @@ function show(io::IO, x::ANY) if !isdefined(x, f) print(io, undef_ref_str) else - show(io, x.(f)) + show(io, x.(i)) end if i < n print(io, ',') diff --git a/src/alloc.c b/src/alloc.c index dc735b0f00b3b..3984b6de6cc1f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -190,19 +190,22 @@ jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i) return jl_new_bits(jl_tupleref(st->types,i), (char*)v + offs); } -int jl_field_isdefined(jl_value_t *v, jl_sym_t *fld, int err) +jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); - int i = jl_field_index(st, fld, err); - if (i == -1) return 0; + if (i >= jl_tuple_len(st->names)) + jl_throw(jl_bounds_exception); size_t offs = jl_field_offset(st,i) + sizeof(void*); if (st->fields[i].isptr) { - return *(jl_value_t**)((char*)v + offs) != NULL; + jl_value_t *fval = *(jl_value_t**)((char*)v + offs); + if (fval == NULL) + jl_throw(jl_undefref_exception); + return fval; } - return 1; + return jl_new_bits(jl_tupleref(st->types,i), (char*)v + offs); } -jl_value_t *jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) +void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); size_t offs = jl_field_offset(st,i) + sizeof(void*); @@ -212,7 +215,16 @@ jl_value_t *jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) else { jl_assign_bits((char*)v + offs, rhs); } - return rhs; +} + +int jl_field_isdefined(jl_value_t *v, size_t i) +{ + jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); + size_t offs = jl_field_offset(st,i) + sizeof(void*); + if (st->fields[i].isptr) { + return *(jl_value_t**)((char*)v + offs) != NULL; + } + return 1; } DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) diff --git a/src/builtins.c b/src/builtins.c index 2c860b4001d5d..d3378dbfdaad9 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -393,17 +393,29 @@ JL_CALLABLE(jl_f_isdefined) JL_NARGS(isdefined, 1, 1); } else { - JL_TYPECHK(isdefined, symbol, args[1]); - s = (jl_sym_t*)args[1]; if (!jl_is_module(args[0])) { - jl_value_t *vt = (jl_value_t*)jl_typeof(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]); } - return jl_field_isdefined(args[0], s, 0) ? jl_true : jl_false; + size_t idx; + if (jl_is_long(args[1])) { + idx = jl_unbox_long(args[1])-1; + if (idx >= jl_tuple_len(vt->names)) + return jl_false; + } + else { + JL_TYPECHK(isdefined, symbol, args[1]); + idx = jl_field_index(vt, (jl_sym_t*)args[1], 0); + if ((int)idx == -1) + return jl_false; + } + return jl_field_isdefined(args[0], idx) ? jl_true : jl_false; } JL_TYPECHK(isdefined, module, args[0]); + JL_TYPECHK(isdefined, symbol, args[1]); m = (jl_module_t*)args[0]; + s = (jl_sym_t*)args[1]; } assert(s); return jl_boundp(m, s) ? jl_true : jl_false; @@ -446,18 +458,27 @@ JL_CALLABLE(jl_f_tuplelen) JL_CALLABLE(jl_f_get_field) { JL_NARGS(getfield, 2, 2); - JL_TYPECHK(getfield, symbol, args[1]); jl_value_t *v = args[0]; jl_value_t *vt = (jl_value_t*)jl_typeof(v); if (vt == (jl_value_t*)jl_module_type) { + JL_TYPECHK(getfield, symbol, args[1]); return jl_eval_global_var((jl_module_t*)v, (jl_sym_t*)args[1]); } - if (!jl_is_datatype(vt)) { + if (!jl_is_datatype(vt)) jl_type_error("getfield", (jl_value_t*)jl_datatype_type, v); - } jl_datatype_t *st = (jl_datatype_t*)vt; - jl_sym_t *fld = (jl_sym_t*)args[1]; - jl_value_t *fval = jl_get_nth_field(v, jl_field_index(st, fld, 1)); + size_t idx; + if (jl_is_long(args[1])) { + idx = jl_unbox_long(args[1])-1; + if (idx >= jl_tuple_len(st->names)) + jl_throw(jl_bounds_exception); + } + else { + JL_TYPECHK(getfield, symbol, args[1]); + jl_sym_t *fld = (jl_sym_t*)args[1]; + idx = jl_field_index(st, fld, 1); + } + jl_value_t *fval = jl_get_nth_field(v, idx); if (fval == NULL) jl_throw(jl_undefref_exception); return fval; @@ -466,7 +487,6 @@ JL_CALLABLE(jl_f_get_field) JL_CALLABLE(jl_f_set_field) { JL_NARGS(setfield, 3, 3); - JL_TYPECHK(setfield, symbol, args[1]); jl_value_t *v = args[0]; jl_value_t *vt = (jl_value_t*)jl_typeof(v); if (vt == (jl_value_t*)jl_module_type) @@ -476,13 +496,21 @@ JL_CALLABLE(jl_f_set_field) jl_datatype_t *st = (jl_datatype_t*)vt; if (!st->mutabl) jl_errorf("type %s is immutable", st->name->name->name); - jl_sym_t *fld = (jl_sym_t*)args[1]; - size_t i = jl_field_index(st, fld, 1); - jl_value_t *ft = jl_tupleref(st->types,i); + size_t idx; + if (jl_is_long(args[1])) { + idx = jl_unbox_long(args[1])-1; + if (idx >= jl_tuple_len(st->names)) + jl_throw(jl_bounds_exception); + } + else { + JL_TYPECHK(setfield, symbol, args[1]); + idx = jl_field_index(st, (jl_sym_t*)args[1], 1); + } + jl_value_t *ft = jl_tupleref(st->types, idx); if (!jl_subtype(args[2], ft, 1)) { jl_type_error("setfield", ft, args[2]); } - jl_set_nth_field(v, i, args[2]); + jl_set_nth_field(v, idx, args[2]); return args[2]; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 3e22cb145bd10..59ea26ad9d00d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -226,6 +226,9 @@ static Function *box64_func; static Function *jlputs_func; static Function *jldlsym_func; static Function *jlnewbits_func; +//static Function *jlgetnthfield_func; +static Function *jlgetnthfieldchecked_func; +//static Function *jlsetnthfield_func; #ifdef _OS_WINDOWS_ static Function *resetstkoflw_func; #endif @@ -1227,6 +1230,33 @@ static Value *emit_f_is(jl_value_t *rt1, jl_value_t *rt2, return answer; } +static bool is_structtype_all_pointers(jl_datatype_t *dt) +{ + jl_tuple_t *t = dt->types; + size_t i, l = jl_tuple_len(t); + for(i=0; i < l; i++) { + if (!dt->fields[i].isptr) + return false; + } + return true; +} + +static bool is_structtype_homogeneous(jl_datatype_t *dt) +{ + jl_tuple_t *t = dt->types; + size_t i, l = jl_tuple_len(t); + if (l > 0) { + jl_value_t *t0 = jl_tupleref(t, 0); + if (!jl_is_leaf_type(t0)) + return false; + for(i=1; i < l; i++) { + if (!jl_types_equal(t0, jl_tupleref(t,i))) + return false; + } + } + return true; +} + static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, jl_codectx_t *ctx, Value **theFptr, jl_function_t **theF, @@ -1675,6 +1705,77 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, JL_GC_POP(); return fld; } + jl_datatype_t *stt = (jl_datatype_t*)expr_type(args[1], ctx); + jl_value_t *fldt = expr_type(args[2], ctx); + if (jl_is_structtype(stt) && fldt == (jl_value_t*)jl_long_type && !jl_subtype((jl_value_t*)jl_module_type, (jl_value_t*)stt, 0)) { + size_t nfields = jl_tuple_len(stt->names); + // integer index + if (jl_is_long(args[2])) { + // known index + size_t idx = jl_unbox_long(args[2])-1; + if (idx < nfields) { + Value *fld = emit_getfield(args[1], + (jl_sym_t*)jl_tupleref(stt->names, idx), + ctx); + JL_GC_POP(); + return fld; + } + } + else { + // unknown index + Value *strct = emit_expr(args[1], ctx); + Value *idx = emit_unbox(T_size, emit_unboxed(args[2], ctx), (jl_value_t*)jl_long_type); + Type *llvm_st = strct->getType(); + if (llvm_st == jl_pvalue_llvmt) { + if (is_structtype_all_pointers(stt)) { + idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + Value *fld = + builder. + CreateLoad(builder. + CreateGEP(builder. + CreateBitCast(strct, jl_ppvalue_llvmt), + builder.CreateAdd(idx,ConstantInt::get(T_size,1)))); + null_pointer_check(fld, ctx); + JL_GC_POP(); + return fld; + } + else if (is_structtype_homogeneous(stt)) { + assert(nfields > 0); // nf==0 trapped by all_pointers case + jl_value_t *jt = jl_t0(stt->types); + idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + Value *ptr = data_pointer(strct); + JL_GC_POP(); + return typed_load(ptr, idx, jt, ctx); + } + else { + idx = builder.CreateSub(idx, ConstantInt::get(T_size, 1)); + Value *fld = builder.CreateCall2(jlgetnthfieldchecked_func, strct, idx); + JL_GC_POP(); + return fld; + } + } + else if (is_structtype_homogeneous(stt)) { + assert(llvm_st->isStructTy()); + // TODO: move these allocas to the first basic block instead of + // frobbing the stack + Instruction *stacksave = + CallInst::Create(Intrinsic::getDeclaration(jl_Module, + Intrinsic::stacksave)); + builder.Insert(stacksave); + Value *tempSpace = builder.CreateAlloca(llvm_st); + builder.CreateStore(strct, tempSpace); + jl_value_t *jt = jl_t0(stt->types); + idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + Value *ptr = builder.CreateGEP(tempSpace, ConstantInt::get(T_size, 0)); + Value *fld = typed_load(ptr, idx, jt, ctx); + builder.CreateCall(Intrinsic::getDeclaration(jl_Module, + Intrinsic::stackrestore), + stacksave); + JL_GC_POP(); + return fld; + } + } + } } else if (f->fptr == &jl_f_set_field && nargs==3) { jl_datatype_t *sty = (jl_datatype_t*)expr_type(args[1], ctx); @@ -1701,6 +1802,7 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, } } } + // TODO: faster code for integer index } else if (f->fptr == &jl_f_instantiate_type && nargs > 0) { size_t i; @@ -3752,6 +3854,15 @@ static void init_julia_llvm_env(Module *m) "jl_new_bits", jl_Module); jl_ExecutionEngine->addGlobalMapping(jlnewbits_func, (void*)&jl_new_bits); + std::vector getnthfld_args(0); + getnthfld_args.push_back(jl_pvalue_llvmt); + getnthfld_args.push_back(T_size); + jlgetnthfieldchecked_func = + Function::Create(FunctionType::get(jl_pvalue_llvmt, getnthfld_args, false), + Function::ExternalLinkage, + "jl_get_nth_field_checked", jl_Module); + jl_ExecutionEngine->addGlobalMapping(jlgetnthfieldchecked_func, (void*)*jl_get_nth_field_checked); + // set up optimization passes FPM = new FunctionPassManager(jl_Module); diff --git a/src/julia.h b/src/julia.h index 35bd50a61ea82..1b7cc12a79fbb 100644 --- a/src/julia.h +++ b/src/julia.h @@ -778,8 +778,9 @@ DLLEXPORT void *jl_unbox_voidpointer(jl_value_t *v); void jl_compute_field_offsets(jl_datatype_t *st); int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err); DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i); -jl_value_t *jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs); -int jl_field_isdefined(jl_value_t *v, jl_sym_t *fld, int err); +DLLEXPORT jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i); +DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs); +DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i); // arrays DLLEXPORT jl_array_t *jl_new_array(jl_value_t *atype, jl_tuple_t *dims); diff --git a/test/core.jl b/test/core.jl index cc111d8975b97..c60a2394d49ad 100644 --- a/test/core.jl +++ b/test/core.jl @@ -378,14 +378,16 @@ begin @test isdefined(a) @test !isdefined(a,2) - @test isdefined("a",:data) a = UndefField() @test !isdefined(a, :field) @test !isdefined(a, :foo) @test !isdefined(2, :a) + @test isdefined("a",:data) + @test isdefined("a", 1) + @test !isdefined("a", 2) + @test_throws isdefined(2) - @test_throws isdefined("a", 2) end # dispatch @@ -518,6 +520,30 @@ begin @test b == 1 end +# accessing fields by index +begin + local z = complex(3, 4) + v = Int[0,0] + for i=1:2 + v[i] = getfield(z, i) + end + @test v == [3,4] + @test_throws getfield(z, -1) + @test_throws getfield(z, 0) + @test_throws getfield(z, 3) + + strct = LoadError("", 0, "") + setfield(strct, 2, 8) + @test strct.line == 8 + setfield(strct, 3, "hi") + @test strct.error == "hi" + setfield(strct, 1, "yo") + @test strct.file == "yo" + @test_throws getfield(strct, 10) + @test_throws setfield(strct, 0, "") + @test_throws setfield(strct, 4, "") +end + # allow typevar in Union to match as long as the arguments contain # sufficient information # issue #814