package reflect; fix substring type bug; fix scoping rules for using on procedure parameter

This commit is contained in:
gingerBill
2019-08-11 23:58:49 +01:00
parent b08aa857b3
commit 04036aba9c
5 changed files with 391 additions and 57 deletions
+285
View File
@@ -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;
}
+7 -4
View File
@@ -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; if info == nil do return nil;
base := info; base := info;
loop: for { loop: for {
switch i in base.variant { switch i in base.variant {
case Type_Info_Named: base = i.base; case Type_Info_Named: base = i.base;
case Type_Info_Enum: base = i.base; case Type_Info_Enum: base = i.base;
case Type_Info_Opaque: base = i.elem;
case: break loop; case: break loop;
} }
} }
return base; return base;
} }
type_info_base_without_enum :: type_info_core;
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info { __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
data := transmute(Typeid_Bit_Field)id; data := transmute(Typeid_Bit_Field)id;
@@ -311,10 +313,11 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid {
ti = type_info_base(ti); ti = type_info_base(ti);
return ti.id; 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)); ti := type_info_base_without_enum(type_info_of(id));
return ti.id; return ti.id;
} }
typeid_base_without_enum :: typeid_core;
+24 -10
View File
@@ -4,6 +4,7 @@ import "core:fmt"
import "core:mem" import "core:mem"
import "core:os" import "core:os"
import "core:runtime" import "core:runtime"
import "core:reflect"
when os.OS == "windows" { when os.OS == "windows" {
import "core:thread" import "core:thread"
@@ -946,26 +947,39 @@ deferred_procedure_associations :: proc() {
} }
} }
struct_field_tags :: proc() { reflection :: proc() {
fmt.println("# struct_field_tags"); fmt.println("# reflection");
Foo :: struct { Foo :: struct {
x: int `tag1`, x: int `tag1`,
y: string `tag2`, y: string `json:"y_field"`,
z: bool, // no tag z: bool, // no tag
} }
ti := runtime.type_info_base(type_info_of(Foo)); id := typeid_of(Foo);
s := ti.variant.(runtime.Type_Info_Struct); 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 {"); fmt.println("Foo :: struct {");
for _, i in s.names { for tag, i in tags {
if tag := s.tags[i]; tag != "" { name, type := names[i], types[i];
fmt.printf("\t%s: %T `%s`,\n", s.names[i], s.types[i], tag); if tag != "" {
fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
} else { } else {
fmt.printf("\t%s: %T,\n", s.names[i], s.types[i]); fmt.printf("\t%s: %T,\n", name, type);
} }
} }
fmt.println("}"); 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() { main :: proc() {
@@ -986,6 +1000,6 @@ main :: proc() {
bit_set_type(); bit_set_type();
diverging_procedures(); diverging_procedures();
deferred_procedure_associations(); deferred_procedure_associations();
struct_field_tags(); reflection();
} }
} }
+74 -42
View File
@@ -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) { void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
if (body == nullptr) { 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_decl = decl;
ctx->curr_proc_sig = type; ctx->curr_proc_sig = type;
GB_ASSERT(type->kind == Type_Proc); ast_node(bs, BlockStmt, body);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple; Array<ProcUsingVar> using_entities = {};
for_array(i, params->variables) { using_entities.allocator = heap_allocator();
Entity *e = params->variables[i]; defer (array_free(&using_entities));
if (e->kind != Entity_Variable) {
continue; {
} GB_ASSERT(type->kind == Type_Proc);
if (!(e->flags & EntityFlag_Using)) { if (type->Proc.param_count > 0) {
continue; TypeTuple *params = &type->Proc.params->Tuple;
} for_array(i, params->variables) {
bool is_immutable = e->Variable.is_immutable; Entity *e = params->variables[i];
bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); if (e->kind != Entity_Variable) {
String name = e->token.string; continue;
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); if (!(e->flags & EntityFlag_Using)) {
for_array(i, scope->elements.entries) { continue;
Entity *f = scope->elements.entries[i].value; }
if (f->kind == Entity_Variable) { bool is_immutable = e->Variable.is_immutable;
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
uvar->Variable.is_immutable = is_immutable; String name = e->token.string;
if (is_value) uvar->flags |= EntityFlag_Value; 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);
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;
} }
} }
} 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_open_scope(ctx, body);
check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); {
if (type->Proc.result_count > 0) { for_array(i, using_entities) {
if (!check_is_terminating(body)) { Entity *e = using_entities[i].e;
if (token.kind == Token_Ident) { Entity *uvar = using_entities[i].uvar;
error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); Entity *prev = scope_insert(ctx->scope, uvar);
} else { // NOTE(bill): Don't err here
// NOTE(bill): Anonymous procedure (lambda) }
error(bs->close, "Missing return statement at the end of the procedure");
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");
}
} }
} }
} }
+1 -1
View File
@@ -7164,7 +7164,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (o->mode == Addressing_Constant) { if (o->mode == Addressing_Constant) {
max_count = o->value.value_string.len; max_count = o->value.value_string.len;
} }
o->type = t_string; o->type = type_deref(o->type);
} }
break; break;