aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2019-08-11 23:58:49 +0100
committergingerBill <bill@gingerbill.org>2019-08-11 23:58:49 +0100
commit04036aba9c1eec05fe143d939ef93e017502f015 (patch)
tree0d9da264ef729dc01822a6be975cfb8f03fc531f
parentb08aa857b3c99c241e93ea3d0fbe36f712d96015 (diff)
`package reflect`; fix substring type bug; fix scoping rules for `using` on procedure parameter
-rw-r--r--core/reflect/reflect.odin285
-rw-r--r--core/runtime/core.odin11
-rw-r--r--examples/demo/demo.odin34
-rw-r--r--src/check_decl.cpp118
-rw-r--r--src/check_expr.cpp2
5 files changed, 392 insertions, 58 deletions
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
new file mode 100644
index 000000000..ecc2e7362
--- /dev/null
+++ b/core/reflect/reflect.odin
@@ -0,0 +1,285 @@
+package reflect
+
+import "core:runtime"
+import "core:mem"
+
+
+Type_Kind :: enum {
+ Invalid,
+
+ Named,
+ Integer,
+ Rune,
+ Float,
+ Complex,
+ String,
+ Boolean,
+ Any,
+ Type_Id,
+ Pointer,
+ Procedure,
+ Array,
+ Dynamic_Array,
+ Slice,
+ Tuple,
+ Struct,
+ Union,
+ Enum,
+ Map,
+ Bit_Field,
+ Bit_Set,
+ Opaque,
+ Simd_Vector,
+}
+
+
+type_kind :: proc(T: typeid) -> Type_Kind {
+ ti := type_info_of(T);
+ if ti != nil {
+ #complete switch _ in ti.variant {
+ case runtime.Type_Info_Named: return .Named;
+ case runtime.Type_Info_Integer: return .Integer;
+ case runtime.Type_Info_Rune: return .Rune;
+ case runtime.Type_Info_Float: return .Float;
+ case runtime.Type_Info_Complex: return .Complex;
+ case runtime.Type_Info_String: return .String;
+ case runtime.Type_Info_Boolean: return .Boolean;
+ case runtime.Type_Info_Any: return .Any;
+ case runtime.Type_Info_Type_Id: return .Type_Id;
+ case runtime.Type_Info_Pointer: return .Pointer;
+ case runtime.Type_Info_Procedure: return .Procedure;
+ case runtime.Type_Info_Array: return .Array;
+ case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array;
+ case runtime.Type_Info_Slice: return .Slice;
+ case runtime.Type_Info_Tuple: return .Tuple;
+ case runtime.Type_Info_Struct: return .Struct;
+ case runtime.Type_Info_Union: return .Union;
+ case runtime.Type_Info_Enum: return .Enum;
+ case runtime.Type_Info_Map: return .Map;
+ case runtime.Type_Info_Bit_Field: return .Bit_Field;
+ case runtime.Type_Info_Bit_Set: return .Bit_Set;
+ case runtime.Type_Info_Opaque: return .Opaque;
+ case runtime.Type_Info_Simd_Vector: return .Simd_Vector;
+ }
+
+ }
+ return .Invalid;
+}
+
+// TODO(bill): Better name
+underlying_type_kind :: proc(T: typeid) -> Type_Kind {
+ return type_kind(runtime.typeid_base(T));
+}
+
+// TODO(bill): Better name
+backing_type_kind :: proc(T: typeid) -> Type_Kind {
+ return type_kind(runtime.typeid_core(T));
+}
+
+
+
+size_of_typeid :: proc(T: typeid) -> int {
+ if ti := type_info_of(T); ti != nil {
+ return ti.size;
+ }
+ return 0;
+}
+
+align_of_typeid :: proc(T: typeid) -> int {
+ if ti := type_info_of(T); ti != nil {
+ return ti.align;
+ }
+ return 1;
+}
+
+to_bytes :: proc(v: any) -> []byte {
+ if v != nil {
+ sz := size_of_typeid(v.id);
+ return mem.slice_ptr((^byte)(v.data), sz);
+ }
+ return nil;
+}
+
+any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) {
+ return v.data, v.id;
+}
+
+is_nil :: proc(v: any) -> bool {
+ data := to_bytes(v);
+ if data != nil {
+ return true;
+ }
+ for v in data do if v != 0 {
+ return false;
+ }
+ return true;
+}
+
+
+index :: proc(v: any, i: int, loc := #caller_location) -> any {
+ if v == nil do return nil;
+
+ v := v;
+ v.id = runtime.typeid_base(v.id);
+ switch a in v {
+ case runtime.Type_Info_Array:
+ runtime.bounds_check_error_loc(loc, i, a.count);
+ offset := uintptr(a.elem.size * i);
+ data := rawptr(uintptr(v.data) + offset);
+ return any{data, a.elem.id};
+
+ case runtime.Type_Info_Slice:
+ raw := (^mem.Raw_Slice)(v.data);
+ runtime.bounds_check_error_loc(loc, i, raw.len);
+ offset := uintptr(a.elem.size * i);
+ data := rawptr(uintptr(raw.data) + offset);
+ return any{data, a.elem.id};
+
+ case runtime.Type_Info_Dynamic_Array:
+ raw := (^mem.Raw_Dynamic_Array)(v.data);
+ runtime.bounds_check_error_loc(loc, i, raw.len);
+ offset := uintptr(a.elem.size * i);
+ data := rawptr(uintptr(raw.data) + offset);
+ return any{data, a.elem.id};
+
+ case runtime.Type_Info_String:
+ if a.is_cstring do return nil;
+
+ raw := (^mem.Raw_String)(v.data);
+ runtime.bounds_check_error_loc(loc, i, raw.len);
+ offset := uintptr(size_of(u8) * i);
+ data := rawptr(uintptr(raw.data) + offset);
+ return any{data, typeid_of(u8)};
+ }
+ return nil;
+}
+
+
+
+
+Struct_Tag :: distinct string;
+
+Struct_Field :: struct {
+ name: string,
+ type: typeid,
+ tag: Struct_Tag,
+ offset: uintptr,
+}
+
+struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
+ ti := runtime.type_info_base(type_info_of(T));
+ if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
+ if 0 <= i && i < len(s.names) {
+ field.name = s.names[i];
+ field.type = s.types[i].id;
+ field.tag = Struct_Tag(s.tags[i]);
+ field.offset = s.offsets[i];
+ }
+ }
+ return;
+}
+
+struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
+ ti := runtime.type_info_base(type_info_of(T));
+ if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
+ for fname, i in s.names {
+ if fname == name {
+ field.name = s.names[i];
+ field.type = s.types[i].id;
+ field.tag = Struct_Tag(s.tags[i]);
+ field.offset = s.offsets[i];
+ break;
+ }
+ }
+ }
+ return;
+}
+
+
+
+struct_field_names :: proc(T: typeid) -> []string {
+ ti := runtime.type_info_base(type_info_of(T));
+ if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
+ return s.names;
+ }
+ return nil;
+}
+
+struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
+ ti := runtime.type_info_base(type_info_of(T));
+ if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
+ return s.types;
+ }
+ return nil;
+}
+
+
+struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
+ ti := runtime.type_info_base(type_info_of(T));
+ if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
+ return transmute([]Struct_Tag)s.tags;
+ }
+ return nil;
+}
+
+struct_field_offsets :: proc(T: typeid) -> []uintptr {
+ ti := runtime.type_info_base(type_info_of(T));
+ if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
+ return s.offsets;
+ }
+ return nil;
+}
+
+
+
+struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
+ value, _ = struct_tag_lookup(tag, key);
+ return;
+}
+
+struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
+ for tag := tag; tag != ""; /**/ {
+ i := 0;
+ for i < len(tag) && tag[i] == ' ' { // Skip whitespace
+ i += 1;
+ }
+ tag = tag[i:];
+ if len(tag) == 0 do break;
+
+ i = 0;
+ loop: for i < len(tag) {
+ switch tag[i] {
+ case ':', '"':
+ break loop;
+ case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
+ break loop;
+ }
+ i += 1;
+ }
+
+ if i == 0 do break;
+ if i+1 >= len(tag) do break;
+
+ if tag[i] != ':' || tag[i+1] != '"' {
+ break;
+ }
+ name := string(tag[:i]);
+ tag = tag[i+1:];
+
+ i = 1;
+ for i < len(tag) && tag[i] != '"' { // find closing quote
+ if tag[i] == '\\' do i += 1; // Skip escaped characters
+ i += 1;
+ }
+
+ if i >= len(tag) do break;
+
+ val := string(tag[:i+1]);
+ tag = tag[i+1:];
+
+ if key == name {
+ return val[1:i], true;
+ }
+ }
+ return;
+}
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index 28767cc2d..047281b10 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -283,19 +283,21 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
}
-type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
+type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
if info == nil do return nil;
base := info;
loop: for {
switch i in base.variant {
- case Type_Info_Named: base = i.base;
- case Type_Info_Enum: base = i.base;
+ case Type_Info_Named: base = i.base;
+ case Type_Info_Enum: base = i.base;
+ case Type_Info_Opaque: base = i.elem;
case: break loop;
}
}
return base;
}
+type_info_base_without_enum :: type_info_core;
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
data := transmute(Typeid_Bit_Field)id;
@@ -311,10 +313,11 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid {
ti = type_info_base(ti);
return ti.id;
}
-typeid_base_without_enum :: proc "contextless" (id: typeid) -> typeid {
+typeid_core :: proc "contextless" (id: typeid) -> typeid {
ti := type_info_base_without_enum(type_info_of(id));
return ti.id;
}
+typeid_base_without_enum :: typeid_core;
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index e07eea7c7..157ecb9bf 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -4,6 +4,7 @@ import "core:fmt"
import "core:mem"
import "core:os"
import "core:runtime"
+import "core:reflect"
when os.OS == "windows" {
import "core:thread"
@@ -946,26 +947,39 @@ deferred_procedure_associations :: proc() {
}
}
-struct_field_tags :: proc() {
- fmt.println("# struct_field_tags");
+reflection :: proc() {
+ fmt.println("# reflection");
Foo :: struct {
x: int `tag1`,
- y: string `tag2`,
+ y: string `json:"y_field"`,
z: bool, // no tag
}
- ti := runtime.type_info_base(type_info_of(Foo));
- s := ti.variant.(runtime.Type_Info_Struct);
+ id := typeid_of(Foo);
+ names := reflect.struct_field_names(id);
+ types := reflect.struct_field_types(id);
+ tags := reflect.struct_field_tags(id);
+
+ assert(len(names) == len(types) && len(names) == len(tags));
+
fmt.println("Foo :: struct {");
- for _, i in s.names {
- if tag := s.tags[i]; tag != "" {
- fmt.printf("\t%s: %T `%s`,\n", s.names[i], s.types[i], tag);
+ for tag, i in tags {
+ name, type := names[i], types[i];
+ if tag != "" {
+ fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
} else {
- fmt.printf("\t%s: %T,\n", s.names[i], s.types[i]);
+ fmt.printf("\t%s: %T,\n", name, type);
}
}
fmt.println("}");
+
+
+ for tag, i in tags {
+ if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
+ fmt.printf("json: %s -> %s\n", names[i], val);
+ }
+ }
}
main :: proc() {
@@ -986,6 +1000,6 @@ main :: proc() {
bit_set_type();
diverging_procedures();
deferred_procedure_associations();
- struct_field_tags();
+ reflection();
}
}
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index f3e0f70bc..cf3cb8e97 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1074,6 +1074,11 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_
}
+struct ProcUsingVar {
+ Entity *e;
+ Entity *uvar;
+};
+
void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
if (body == nullptr) {
@@ -1098,58 +1103,85 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
ctx->curr_proc_decl = decl;
ctx->curr_proc_sig = type;
- GB_ASSERT(type->kind == Type_Proc);
- if (type->Proc.param_count > 0) {
- TypeTuple *params = &type->Proc.params->Tuple;
- for_array(i, params->variables) {
- Entity *e = params->variables[i];
- if (e->kind != Entity_Variable) {
- continue;
- }
- if (!(e->flags & EntityFlag_Using)) {
- continue;
- }
- bool is_immutable = e->Variable.is_immutable;
- bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
- String name = e->token.string;
- Type *t = base_type(type_deref(e->type));
- if (t->kind == Type_Struct) {
- Scope *scope = t->Struct.scope;
- if (scope == nullptr) {
- scope = scope_of_node(t->Struct.node);
+ ast_node(bs, BlockStmt, body);
+
+ Array<ProcUsingVar> using_entities = {};
+ using_entities.allocator = heap_allocator();
+ defer (array_free(&using_entities));
+
+ {
+ GB_ASSERT(type->kind == Type_Proc);
+ if (type->Proc.param_count > 0) {
+ TypeTuple *params = &type->Proc.params->Tuple;
+ for_array(i, params->variables) {
+ Entity *e = params->variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
}
- GB_ASSERT(scope != nullptr);
- for_array(i, scope->elements.entries) {
- Entity *f = scope->elements.entries[i].value;
- if (f->kind == Entity_Variable) {
- Entity *uvar = alloc_entity_using_variable(e, f->token, f->type);
- uvar->Variable.is_immutable = is_immutable;
- if (is_value) uvar->flags |= EntityFlag_Value;
-
- Entity *prev = scope_insert(ctx->scope, uvar);
- if (prev != nullptr) {
- error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string));
- break;
+ if (!(e->flags & EntityFlag_Using)) {
+ continue;
+ }
+ bool is_immutable = e->Variable.is_immutable;
+ bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
+ String name = e->token.string;
+ Type *t = base_type(type_deref(e->type));
+ if (t->kind == Type_Struct) {
+ Scope *scope = t->Struct.scope;
+ if (scope == nullptr) {
+ scope = scope_of_node(t->Struct.node);
+ }
+ GB_ASSERT(scope != nullptr);
+ for_array(i, scope->elements.entries) {
+ Entity *f = scope->elements.entries[i].value;
+ if (f->kind == Entity_Variable) {
+ Entity *uvar = alloc_entity_using_variable(e, f->token, f->type);
+ uvar->Variable.is_immutable = is_immutable;
+ if (is_value) uvar->flags |= EntityFlag_Value;
+
+
+ ProcUsingVar puv = {e, uvar};
+ array_add(&using_entities, puv);
+
}
}
+ } else {
+ error(e->token, "'using' can only be applied to variables of type struct");
+ break;
}
- } else {
- error(e->token, "'using' can only be applied to variables of type struct");
- break;
}
}
}
- ast_node(bs, BlockStmt, body);
+
+ for_array(i, using_entities) {
+ Entity *e = using_entities[i].e;
+ Entity *uvar = using_entities[i].uvar;
+ Entity *prev = scope_insert(ctx->scope, uvar);
+ if (prev != nullptr) {
+ error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
+ break;
+ }
+ }
+
check_open_scope(ctx, body);
- check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
- if (type->Proc.result_count > 0) {
- if (!check_is_terminating(body)) {
- if (token.kind == Token_Ident) {
- error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string));
- } else {
- // NOTE(bill): Anonymous procedure (lambda)
- error(bs->close, "Missing return statement at the end of the procedure");
+ {
+ for_array(i, using_entities) {
+ Entity *e = using_entities[i].e;
+ Entity *uvar = using_entities[i].uvar;
+ Entity *prev = scope_insert(ctx->scope, uvar);
+ // NOTE(bill): Don't err here
+ }
+
+ check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
+
+ if (type->Proc.result_count > 0) {
+ if (!check_is_terminating(body)) {
+ if (token.kind == Token_Ident) {
+ error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string));
+ } else {
+ // NOTE(bill): Anonymous procedure (lambda)
+ error(bs->close, "Missing return statement at the end of the procedure");
+ }
}
}
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 99fe6695b..7a6e0f22d 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -7164,7 +7164,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (o->mode == Addressing_Constant) {
max_count = o->value.value_string.len;
}
- o->type = t_string;
+ o->type = type_deref(o->type);
}
break;