Permalink
Browse files

allow struct field access by index. closes #4806

  • Loading branch information...
1 parent 8cd1702 commit bff951e1724c706eb74f82faae7f5025c78eb8b8 @JeffBezanson JeffBezanson committed Jan 13, 2014
Showing with 224 additions and 34 deletions.
  1. +3 −0 NEWS.md
  2. +3 −3 base/deepcopy.jl
  3. +9 −0 base/inference.jl
  4. +5 −5 base/serialize.jl
  5. +1 −1 base/show.jl
  6. +19 −7 src/alloc.c
  7. +42 −14 src/builtins.c
  8. +111 −0 src/codegen.cpp
  9. +3 −2 src/julia.h
  10. +28 −2 test/core.jl
View
@@ -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
==========================
View
@@ -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
View
@@ -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
View
@@ -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
View
@@ -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, ',')
View
@@ -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, ...)
View
@@ -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];
}
View
@@ -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
@vtjnash

vtjnash Jan 13, 2014

Member

is that a word? google search results seem to indicate this means julia now acts like a typical teenager

Urban Dictionary: frobbing
www.urbandictionary.com/define.php?term=frobbing‎
To spend excessive time on a computer surfing the net ...
@JeffBezanson

JeffBezanson Jan 13, 2014

Owner

That's not a meaning I'm aware of. Check the hacker's dictionary.

@jiahao

jiahao Jan 13, 2014

Member

Presumably this isn't the OED definition either, which is as a synonym of 'throb':

1542 My hart frobbed exceedingly.

+ 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<Type *> 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);
View
@@ -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);
Oops, something went wrong.

0 comments on commit bff951e

Please sign in to comment.