2
0
Эх сурвалжийг харах

`typeid` as keyword (ready to implement polymorphic name parameters)

gingerBill 7 жил өмнө
parent
commit
220485a2d2

+ 33 - 32
core/fmt/fmt.odin

@@ -8,6 +8,7 @@ import "core:types"
 import "core:strconv"
 
 
+@(private)
 DEFAULT_BUFFER_SIZE :: 1<<12;
 
 String_Buffer :: distinct [dynamic]byte;
@@ -238,7 +239,7 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) {
 	case runtime.Type_Info_Named:
 		write_string(buf, info.name);
 	case runtime.Type_Info_Integer:
-		a := any{typeid = typeid_of(ti)};
+		a := any{id = ti.id};
 		switch in a {
 		case int:     write_string(buf, "int");
 		case uint:    write_string(buf, "uint");
@@ -262,7 +263,7 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) {
 			write_string(buf, "string");
 		}
 	case runtime.Type_Info_Boolean:
-		a := any{typeid = typeid_of(ti)};
+		a := any{id = ti.id};
 		switch in a {
 		case bool: write_string(buf, "bool");
 		case:
@@ -462,7 +463,7 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
 	ok := true;
 	if arg_index < len(args) {
 		arg := args[arg_index];
-		arg.typeid = runtime.typeid_base(arg.typeid);
+		arg.id = runtime.typeid_base(arg.id);
 		switch i in arg {
 		case int:  num = i;
 		case i8:   num = int(i);
@@ -486,8 +487,8 @@ fmt_bad_verb :: proc(using fi: ^Fmt_Info, verb: rune) {
 	write_string(buf, "%!");
 	write_rune(buf, verb);
 	write_byte(buf, '(');
-	if arg.typeid != nil {
-		write_typeid(buf, arg.typeid);
+	if arg.id != nil {
+		write_typeid(buf, arg.id);
 		write_byte(buf, '=');
 		fmt_value(fi, arg, 'v');
 	} else {
@@ -721,8 +722,8 @@ fmt_pointer :: proc(fi: ^Fmt_Info, p: rawptr, verb: rune) {
 }
 
 enum_value_to_string :: proc(v: any) -> (string, bool) {
-	v.typeid = runtime.typeid_base(v.typeid);
-	type_info := type_info_of(v.typeid);
+	v.id = runtime.typeid_base(v.id);
+	type_info := type_info_of(v.id);
 
 	switch e in type_info.variant {
 	case: return "", false;
@@ -746,7 +747,7 @@ enum_value_to_string :: proc(v: any) -> (string, bool) {
 			return "", false;
 		}
 
-		a := any{v.data, typeid_of(runtime.type_info_base(e.base))};
+		a := any{v.data, runtime.type_info_base(e.base).id};
 		switch v in a {
 		case rune:    return get_str(v, e);
 		case i8:      return get_str(v, e);
@@ -781,19 +782,19 @@ string_to_enum_value :: proc(T: type, s: string) -> (T, bool) {
 }
 
 fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
-	if v.typeid == nil || v.data == nil {
+	if v.id == nil || v.data == nil {
 		write_string(fi.buf, "<nil>");
 		return;
 	}
 
-	type_info := type_info_of(v.typeid);
+	type_info := type_info_of(v.id);
 	switch e in type_info.variant {
 	case: fmt_bad_verb(fi, verb);
 	case runtime.Type_Info_Enum:
 		switch verb {
 		case: fmt_bad_verb(fi, verb);
 		case 'd', 'f':
-			fmt_arg(fi, any{v.data, typeid_of(runtime.type_info_base(e.base))}, verb);
+			fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb);
 		case 's', 'v':
 			str, ok := enum_value_to_string(v);
 			if !ok do str = "!%(BAD ENUM VALUE)";
@@ -823,11 +824,11 @@ enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 {
 }
 
 fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
-	type_info := type_info_of(v.typeid);
+	type_info := type_info_of(v.id);
 	switch info in type_info.variant {
 	case runtime.Type_Info_Named:
 		val := v;
-		val.typeid = info.base.id;
+		val.id = info.base.id;
 		fmt_bit_set(fi, val, info.name);
 
 	case runtime.Type_Info_Bit_Set:
@@ -877,11 +878,11 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
 	}
 }
 fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
-	type_info := type_info_of(v.typeid);
+	type_info := type_info_of(v.id);
 	switch info in type_info.variant {
 	case runtime.Type_Info_Named:
 		val := v;
-		val.typeid = info.base.id;
+		val.id = info.base.id;
 		fmt_bit_field(fi, val, info.name);
 	case runtime.Type_Info_Bit_Field:
 		data: u64 = 0;
@@ -923,12 +924,12 @@ fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
 }
 
 fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
-	if v.data == nil || v.typeid == nil {
+	if v.data == nil || v.id == nil {
 		write_string(fi.buf, "<nil>");
 		return;
 	}
 
-	type_info := type_info_of(v.typeid);
+	type_info := type_info_of(v.id);
 	switch info in type_info.variant {
 	case runtime.Type_Info_Named:
 		switch b in info.base.variant {
@@ -964,7 +965,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 					write_string(fi.buf, "any{}");
 				} else {
 					data := rawptr(uintptr(v.data) + b.offsets[i]);
-					fmt_arg(fi, any{data, typeid_of(t)}, 'v');
+					fmt_arg(fi, any{data, t.id}, 'v');
 				}
 
 				if hash do write_string(fi.buf, ",\n");
@@ -978,7 +979,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 		case runtime.Type_Info_Bit_Field:
 			fmt_bit_field(fi, v);
 		case:
-			fmt_value(fi, any{v.data, typeid_of(info.base)}, verb);
+			fmt_value(fi, any{v.data, info.base.id}, verb);
 		}
 
 	case runtime.Type_Info_Boolean:    fmt_arg(fi, v, verb);
@@ -989,7 +990,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 	case runtime.Type_Info_String:     fmt_arg(fi, v, verb);
 
 	case runtime.Type_Info_Pointer:
-		if v.typeid == typeid_of(^runtime.Type_Info) {
+		if v.id == typeid_of(^runtime.Type_Info) {
 			write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
 		} else {
 			fmt_pointer(fi, (^rawptr)(v.data)^, verb);
@@ -1002,7 +1003,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 			if i > 0 do write_string(fi.buf, ", ");
 
 			data := uintptr(v.data) + uintptr(i*info.elem_size);
-			fmt_arg(fi, any{rawptr(data), typeid_of(info.elem)}, verb);
+			fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
 		}
 
 	case runtime.Type_Info_Dynamic_Array:
@@ -1013,7 +1014,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 			if i > 0 do write_string(fi.buf, ", ");
 
 			data := uintptr(array.data) + uintptr(i*info.elem_size);
-			fmt_arg(fi, any{rawptr(data), typeid_of(info.elem)}, verb);
+			fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
 		}
 
 	case runtime.Type_Info_Slice:
@@ -1024,7 +1025,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 			if i > 0 do write_string(fi.buf, ", ");
 
 			data := uintptr(slice.data) + uintptr(i*info.elem_size);
-			fmt_arg(fi, any{rawptr(data), typeid_of(info.elem)}, verb);
+			fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
 		}
 
 	case runtime.Type_Info_Map:
@@ -1057,13 +1058,13 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 					write_string(fi.buf, header.key.str);
 				} else {
 					fi := Fmt_Info{buf = fi.buf};
-					fmt_arg(&fi, any{rawptr(&header.key.hash), typeid_of(info.key)}, 'v');
+					fmt_arg(&fi, any{rawptr(&header.key.hash), info.key.id}, 'v');
 				}
 
 				write_string(fi.buf, "=");
 
 				value := data + entry_type.offsets[2];
-				fmt_arg(fi, any{rawptr(value), typeid_of(info.value)}, 'v');
+				fmt_arg(fi, any{rawptr(value), info.value.id}, 'v');
 			}
 		}
 
@@ -1098,14 +1099,14 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 				write_string(fi.buf, "any{}");
 			} else {
 				data := uintptr(v.data) + info.offsets[i];
-				fmt_arg(fi, any{rawptr(data), typeid_of(t)}, 'v');
+				fmt_arg(fi, any{rawptr(data), t.id}, 'v');
 			}
 			if hash do write_string(fi.buf, ",\n");
 		}
 
 	case runtime.Type_Info_Union:
 		tag_ptr := uintptr(v.data) + info.tag_offset;
-		tag_any := any{rawptr(tag_ptr), typeid_of(info.tag_type)};
+		tag_any := any{rawptr(tag_ptr), info.tag_type.id};
 
 		tag: i64 = -1;
 		switch i in tag_any {
@@ -1123,7 +1124,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 		if v.data == nil || tag == 0 {
 			write_string(fi.buf, "nil");
 		} else {
-			id := typeid_of(info.variants[tag-1]);
+			id := info.variants[tag-1].id;
 			fmt_arg(fi, any{v.data, id}, verb);
 		}
 
@@ -1135,7 +1136,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 		if ptr == nil {
 			write_string(fi.buf, "nil");
 		} else {
-			write_typeid(fi.buf, v.typeid);
+			write_typeid(fi.buf, v.id);
 			write_string(fi.buf, " @ ");
 			fmt_pointer(fi, ptr, 'p');
 		}
@@ -1177,7 +1178,7 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 	fi.arg = arg;
 
 	if verb == 'T' {
-		ti := type_info_of(arg.typeid);
+		ti := type_info_of(arg.id);
 		switch a in arg {
 		case ^runtime.Type_Info: ti = a;
 		}
@@ -1186,7 +1187,7 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 	}
 
 	base_arg := arg;
-	base_arg.typeid = runtime.typeid_base(base_arg.typeid);
+	base_arg.id = runtime.typeid_base(base_arg.id);
 	switch a in base_arg {
 	case bool:       fmt_bool(fi, bool(a), verb);
 	case b8:         fmt_bool(fi, bool(a), verb);
@@ -1234,7 +1235,7 @@ sbprint :: proc(buf: ^String_Buffer, args: ..any) -> string {
 	fi.buf = buf;
 
 	for arg, i in args {
-		is_string := arg != nil && types.is_string(type_info_of(arg.typeid));
+		is_string := arg != nil && types.is_string(type_info_of(arg.id));
 		if i > 0 && !is_string && !prev_string {
 			write_byte(buf, ' ');
 		}

+ 1 - 1
core/mem/mem.odin

@@ -111,7 +111,7 @@ ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte {
 }
 
 any_to_bytes :: proc "contextless" (val: any) -> []byte {
-	ti := type_info_of(val.typeid);
+	ti := type_info_of(val.id);
 	size := ti != nil ? ti.size : 0;
 	return transmute([]byte)Raw_Slice{val.data, size};
 }

+ 2 - 2
core/mem/raw.odin

@@ -1,8 +1,8 @@
 package mem
 
 Raw_Any :: struct {
-	data:   rawptr,
-	typeid: typeid,
+	data: rawptr,
+	id:   typeid,
 }
 
 Raw_String :: struct {

+ 19 - 6
core/runtime/core.odin

@@ -268,10 +268,6 @@ type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_In
 	return base;
 }
 
-__typeid_of :: proc "contextless" (ti: ^Type_Info) -> typeid {
-	if ti == nil do return nil;
-	return ti.id;
-}
 __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
 	data := transmute(Typeid_Bit_Field)id;
 	n := int(data.index);
@@ -284,11 +280,11 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
 typeid_base :: proc "contextless" (id: typeid) -> typeid {
 	ti := type_info_of(id);
 	ti = type_info_base(ti);
-	return typeid_of(ti);
+	return ti.id;
 }
 typeid_base_without_enum :: proc "contextless" (id: typeid) -> typeid {
 	ti := type_info_base_without_enum(type_info_of(id));
-	return typeid_of(ti);
+	return ti.id;
 }
 
 
@@ -365,6 +361,23 @@ pop :: proc "contextless" (array: ^$T/[dynamic]$E) -> E {
 	return res;
 }
 
+@(builtin)
+unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
+	runtime.bounds_check_error_loc(loc, index, len(array));
+	n := len(array)-1;
+	if index != n {
+		array[index] = array[n];
+	}
+	pop(array);
+}
+
+@(builtin)
+ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
+	runtime.bounds_check_error_loc(loc, index, len(array));
+	copy(array[index:], array[index+1:]);
+	pop(array);
+}
+
 
 @(builtin)
 clear :: proc[clear_dynamic_array, clear_map];

+ 2 - 2
core/runtime/internal.odin

@@ -63,7 +63,7 @@ __print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
 	case Type_Info_Named:
 		os.write_string(fd, info.name);
 	case Type_Info_Integer:
-		a := any{typeid = typeid_of(ti)};
+		a := any{id = ti.id};
 		switch _ in a {
 		case int:     os.write_string(fd, "int");
 		case uint:    os.write_string(fd, "uint");
@@ -83,7 +83,7 @@ __print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
 	case Type_Info_String:
 		os.write_string(fd, "string");
 	case Type_Info_Boolean:
-		a := any{typeid = typeid_of(ti)};
+		a := any{id = ti.id};
 		switch _ in a {
 		case bool: os.write_string(fd, "bool");
 		case:

+ 42 - 15
src/array.cpp

@@ -24,21 +24,27 @@ struct Array {
 	}
 };
 
-template <typename T> void     array_init         (Array<T> *array, gbAllocator const &a);
-template <typename T> void     array_init         (Array<T> *array, gbAllocator const &a, isize count);
-template <typename T> void     array_init         (Array<T> *array, gbAllocator const &a, isize count, isize capacity);
-template <typename T> Array<T> array_make         (gbAllocator const &a);
-template <typename T> Array<T> array_make         (gbAllocator const &a, isize count);
-template <typename T> Array<T> array_make         (gbAllocator const &a, isize count, isize capacity);
-template <typename T> Array<T> array_make_from_ptr(T *data, isize count, isize capacity);
-template <typename T> void     array_free         (Array<T> *array);
-template <typename T> void     array_add          (Array<T> *array, T const &t);
-template <typename T> T        array_pop          (Array<T> *array);
-template <typename T> void     array_clear        (Array<T> *array);
-template <typename T> void     array_reserve      (Array<T> *array, isize capacity);
-template <typename T> void     array_resize       (Array<T> *array, isize count);
-template <typename T> void     array_set_capacity (Array<T> *array, isize capacity);
-template <typename T> Array<T> array_slice        (Array<T> const &array, isize lo, isize hi);
+template <typename T> void     array_init          (Array<T> *array, gbAllocator const &a);
+template <typename T> void     array_init          (Array<T> *array, gbAllocator const &a, isize count);
+template <typename T> void     array_init          (Array<T> *array, gbAllocator const &a, isize count, isize capacity);
+template <typename T> Array<T> array_make          (gbAllocator const &a);
+template <typename T> Array<T> array_make          (gbAllocator const &a, isize count);
+template <typename T> Array<T> array_make          (gbAllocator const &a, isize count, isize capacity);
+template <typename T> Array<T> array_make_from_ptr (T *data, isize count, isize capacity);
+template <typename T> void     array_free          (Array<T> *array);
+template <typename T> void     array_add           (Array<T> *array, T const &t);
+template <typename T> T        array_pop           (Array<T> *array);
+template <typename T> void     array_clear         (Array<T> *array);
+template <typename T> void     array_reserve       (Array<T> *array, isize capacity);
+template <typename T> void     array_resize        (Array<T> *array, isize count);
+template <typename T> void     array_set_capacity  (Array<T> *array, isize capacity);
+template <typename T> Array<T> array_slice         (Array<T> const &array, isize lo, isize hi);
+
+
+template <typename T> void array_ordered_remove  (Array<T> *array, isize index);
+template <typename T> void array_unordered_remove(Array<T> *array, isize index);
+
+
 
 template <typename T>
 T *array_end_ptr(Array<T> *array) {
@@ -201,6 +207,27 @@ gb_inline Array<T> array_slice(Array<T> const &array, isize lo, isize hi) {
 	}
 	return out;
 }
+template <typename T>
+void array_ordered_remove(Array<T> *array, isize index) {
+	GB_ASSERT(0 <= index && index < array->count);
+
+	isize bytes = (gb_size_of(T)*n) * (array->count-(index+1));
+	gb_memmove(array->data+index, array->data+index+1, bytes);
+	array->count -= 1;
+}
+
+template <typename T>
+void array_unordered_remove(Array<T> *array, isize index) {
+	GB_ASSERT(0 <= index && index < array->count);
+
+	isize n = array->count-1;
+	if (index != n) {
+		gb_memmove(array->data+index, array->data+n, gb_size_of(T));
+	}
+	array_pop(array);
+}
+
+
 
 
 #endif

+ 9 - 5
src/check_decl.cpp

@@ -232,12 +232,11 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def)
 	GB_ASSERT(e->type == nullptr);
 
 	DeclInfo *decl = decl_info_of_entity(e);
-	if (decl != nullptr && total_attribute_count(decl) > 0) {
-		error(decl->attributes[0], "Attributes are not allowed on type declarations");
+	if (decl != nullptr) {
+		check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr);
 	}
 
 
-
 	bool is_distinct = is_type_distinct(type_expr);
 	Ast *te = remove_type_alias_clutter(type_expr);
 	e->type = t_invalid;
@@ -254,6 +253,11 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def)
 	check_type_path_pop(ctx);
 
 	named->Named.base = base_type(bt);
+
+	if (is_distinct && is_type_typeid(e->type)) {
+		error(type_expr, "'distinct' cannot be applied to 'typeid'");
+		is_distinct = false;
+	}
 	if (!is_distinct) {
 		e->type = bt;
 		named->Named.base = bt;
@@ -393,8 +397,8 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 
 
 	DeclInfo *decl = decl_info_of_entity(e);
-	if (decl != nullptr && total_attribute_count(decl) > 0) {
-		error(decl->attributes[0], "Attributes are not allowed on constant value declarations");
+	if (decl != nullptr) {
+		check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr);
 	}
 }
 

+ 11 - 5
src/check_expr.cpp

@@ -3200,10 +3200,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		add_type_info_type(c, t);
 
 		t = base_type(t);
-		if (is_operand_value(o) && are_types_identical(t, t_type_info_ptr)) {
-			add_package_dependency(c, "runtime", "__typeid_of");
-		} else if (o.mode != Addressing_Type) {
-			error(expr, "Expected a type or type info for 'typeid_of'");
+		if (o.mode != Addressing_Type) {
+			error(expr, "Expected a type for 'typeid_of'");
 			return false;
 		}
 
@@ -6069,6 +6067,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 		}
 	case_end;
 
+	case Ast_TypeidType:
 	case Ast_TypeType:
 	case Ast_PolyType:
 	case Ast_ProcType:
@@ -6077,7 +6076,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 	case Ast_DynamicArrayType:
 	case Ast_StructType:
 	case Ast_UnionType:
-	// case Ast_RawUnionType:
 	case Ast_EnumType:
 	case Ast_MapType:
 		o->mode = Addressing_Type;
@@ -6512,6 +6510,14 @@ gbString write_expr_to_string(gbString str, Ast *node) {
 		}
 	case_end;
 
+	case_ast_node(tt, TypeidType, node);
+		str = gb_string_appendc(str, "typeid");
+		if (tt->specialization) {
+			str = gb_string_appendc(str, "/");
+			str = write_expr_to_string(str, tt->specialization);
+		}
+	case_end;
+
 	case_ast_node(pt, ProcType, node);
 		str = gb_string_appendc(str, "proc(");
 		str = write_expr_to_string(str, pt->params);

+ 29 - 0
src/check_type.cpp

@@ -1190,6 +1190,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 				if (specialization == t_invalid){
 					specialization = nullptr;
 				}
+				// warning(type_expr, "'type' parameters are deprecated, please use a polymorphic identifier with a type of 'typeid'. For example, '$T: typeid'");
 
 				if (operands != nullptr) {
 					detemine_type_from_operand = true;
@@ -1263,10 +1264,30 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 
 		for_array(j, p->names) {
 			Ast *name = p->names[j];
+
+			bool is_poly_name = false;
+
+			switch (name->kind) {
+			case Ast_Ident:
+				break;
+			case Ast_PolyType:
+				GB_ASSERT(name->PolyType.specialization == nullptr);
+				is_poly_name = true;
+				name = name->PolyType.type;
+				break;
+			}
 			if (!ast_node_expect(name, Ast_Ident)) {
 				continue;
 			}
 
+			if (is_poly_name) {
+				if (type != nullptr && is_type_typeid(type)) {
+					is_type_param = true;
+				} else {
+					error(name, "Polymorphic names are not yet supported for non-typeid parameters");
+				}
+			}
+
 			Entity *param = nullptr;
 			if (is_type_param) {
 				if (operands != nullptr) {
@@ -2022,6 +2043,14 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 		return check_type_internal(ctx, dt->type, type, named_type);
 	case_end;
 
+	case_ast_node(tt, TypeidType, e);
+		e->tav.mode = Addressing_Type;
+		e->tav.type = t_typeid;
+		*type = t_typeid;
+		set_base_type(named_type, *type);
+		return true;
+	case_end;
+
 	case_ast_node(pt, PolyType, e);
 		Ast *ident = pt->type;
 		if (ident->kind != Ast_Ident) {

+ 59 - 1
src/checker.cpp

@@ -1367,7 +1367,6 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 
 	for_array(i, c->info.definitions) {
 		Entity *e = c->info.definitions[i];
-		// if (e->scope->is_global && !is_type_poly_proc(e->type)) { // TODO(bill): is the check enough?
 		if (e->scope == builtin_pkg->scope) { // TODO(bill): is the check enough?
 			if (e->type == nullptr) {
 				add_dependency_to_set(c, e);
@@ -1806,6 +1805,14 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
 	return false;
 }
 
+DECL_ATTRIBUTE_PROC(const_decl_attribute) {
+	if (name == "private") {
+		// NOTE(bill): Handled elsewhere `check_collect_value_decl`
+		return true;
+	}
+	return false;
+}
+
 
 
 
@@ -2025,6 +2032,49 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 
 	ast_node(vd, ValueDecl, decl);
 
+	bool entity_is_private = false;
+	for_array(i, vd->attributes) {
+		Ast *attr = vd->attributes[i];
+		if (attr->kind != Ast_Attribute) continue;
+		auto *elems = &attr->Attribute.elems;
+		for (isize j = 0; j < elems->count; j++) {
+			Ast *elem = (*elems)[j];
+			String name = {};
+			Ast *value = nullptr;
+			switch (elem->kind) {
+			case_ast_node(i, Ident, elem);
+				name = i->token.string;
+			case_end;
+			case_ast_node(fv, FieldValue, elem);
+				GB_ASSERT(fv->field->kind == Ast_Ident);
+				name = fv->field->Ident.token.string;
+				value = fv->value;
+			case_end;
+			default:
+				continue;
+			}
+
+			if (name == "private") {
+				if (value != nullptr) {
+					error(value, "'%.*s' does not expect a value", LIT(name));
+				}
+
+				if (entity_is_private) {
+					error(elem, "Previous declaration of '%.*s'", LIT(name));
+				} else {
+					entity_is_private = true;
+				}
+				array_unordered_remove(elems, j);
+				j -= 1;
+			}
+		}
+	}
+
+	if (entity_is_private && !(c->scope->flags&ScopeFlag_File)) {
+		error(decl, "Attribute 'private' is not allowed on a non file scope entity");
+	}
+
+
 	if (vd->is_mutable) {
 		if (!(c->scope->flags&ScopeFlag_File)) {
 			// NOTE(bill): local scope -> handle later and in order
@@ -2044,6 +2094,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 			Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr, false);
 			e->identifier = name;
 
+			if (entity_is_private) {
+				e->flags |= EntityFlag_NotExported;
+			}
+
 			if (vd->is_using) {
 				vd->is_using = false; // NOTE(bill): This error will be only caught once
 				error(name, "'using' is not allowed at the file scope");
@@ -2145,6 +2199,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 			}
 			e->identifier = name;
 
+			if (entity_is_private) {
+				e->flags |= EntityFlag_NotExported;
+			}
+
 			if (vd->is_using) {
 				if (e->kind == Entity_TypeName && init->kind == Ast_EnumType) {
 					d->is_using = true;

+ 1 - 3
src/entity.cpp

@@ -47,6 +47,7 @@ enum EntityFlag {
 	EntityFlag_PolyConst     = 1<<13,
 	EntityFlag_NotExported   = 1<<14,
 
+
 	EntityFlag_CVarArg       = 1<<20,
 	EntityFlag_AutoCast      = 1<<21,
 };
@@ -113,9 +114,6 @@ struct Entity {
 			String     link_prefix;
 			bool       is_foreign;
 			bool       is_export;
-
-			// bool       default_is_nil;
-			// bool       default_is_location;
 			bool       is_immutable;
 		} Variable;
 		struct {

+ 2 - 2
src/ir.cpp

@@ -5159,7 +5159,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 		for_array(i, ce->args) {
 			Ast *arg = ce->args[i];
 			TypeAndValue tav = type_and_value_of_expr(arg);
-			GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s", expr_to_string(arg));
+			GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr));
 			GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
 			Type *at = tav.type;
 			if (at->kind == Type_Tuple) {
@@ -5973,7 +5973,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
 				ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
 				String field_names[2] = {
 					str_lit("data"),
-					str_lit("typeid"),
+					str_lit("id"),
 				};
 				Type *field_types[2] = {
 					t_rawptr,

+ 56 - 10
src/parser.cpp

@@ -78,6 +78,7 @@ Token ast_token(Ast *node) {
 		return ast_token(node->UnionField.name);
 
 	case Ast_TypeType:         return node->TypeType.token;
+	case Ast_TypeidType:       return node->TypeidType.token;
 	case Ast_HelperType:       return node->HelperType.token;
 	case Ast_DistinctType:     return node->DistinctType.token;
 	case Ast_PolyType:         return node->PolyType.token;
@@ -307,6 +308,9 @@ Ast *clone_ast(Ast *node) {
 		n->UnionField.list = clone_ast(n->UnionField.list);
 		break;
 
+	case Ast_TypeidType:
+		n->TypeidType.specialization = clone_ast(n->TypeidType.specialization);
+		break;
 	case Ast_TypeType:
 		n->TypeType.specialization = clone_ast(n->TypeType.specialization);
 		break;
@@ -409,7 +413,7 @@ void syntax_error(Ast *node, char *fmt, ...) {
 
 bool ast_node_expect(Ast *node, AstKind kind) {
 	if (node->kind != kind) {
-		syntax_error(node, "Expected %.*s, got %.*s", LIT(ast_strings[node->kind]));
+		error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind]));
 		return false;
 	}
 	return true;
@@ -824,6 +828,13 @@ Ast *ast_union_field(AstFile *f, Ast *name, Ast *list) {
 }
 
 
+Ast *ast_typeid_type(AstFile *f, Token token, Ast *specialization) {
+	Ast *result = alloc_ast_node(f, Ast_TypeidType);
+	result->TypeidType.token = token;
+	result->TypeidType.specialization = specialization;
+	return result;
+}
+
 Ast *ast_type_type(AstFile *f, Token token, Ast *specialization) {
 	Ast *result = alloc_ast_node(f, Ast_TypeType);
 	result->TypeType.token = token;
@@ -1354,10 +1365,14 @@ Ast *        parse_block_stmt(AstFile *f, b32 is_when);
 
 
 
-Ast *parse_ident(AstFile *f) {
+Ast *parse_ident(AstFile *f, bool allow_poly_names=false) {
 	Token token = f->curr_token;
 	if (token.kind == Token_Ident) {
 		advance_token(f);
+	} else if (allow_poly_names && token.kind == Token_Dollar) {
+		Token dollar = token;
+		Ast *name = ast_ident(f, expect_token(f, Token_Ident));
+		return ast_poly_type(f, dollar, name, nullptr);
 	} else {
 		token.string = str_lit("_");
 		expect_token(f, Token_Ident);
@@ -1752,6 +1767,15 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		return ast_poly_type(f, token, type, specialization);
 	} break;
 
+	case Token_typeid: {
+		Token token = expect_token(f, Token_typeid);
+		// Ast *specialization = nullptr;
+		// if (allow_token(f, Token_Quo)) {
+		// 	specialization = parse_type(f);
+		// }
+		return ast_typeid_type(f, token, nullptr);
+	} break;
+
 	case Token_type_of: {
 		Ast *i = ast_implicit(f, expect_token(f, Token_type_of));
 		Ast *type = parse_call_expr(f, i);
@@ -2324,11 +2348,11 @@ Array<Ast *> parse_rhs_expr_list(AstFile *f) {
 	return parse_expr_list(f, false);
 }
 
-Array<Ast *> parse_ident_list(AstFile *f) {
+Array<Ast *> parse_ident_list(AstFile *f, bool allow_poly_names) {
 	auto list = array_make<Ast *>(heap_allocator());
 
 	for (;;) {
-		array_add(&list, parse_ident(f));
+		array_add(&list, parse_ident(f, allow_poly_names));
 		if (f->curr_token.kind != Token_Comma ||
 		    f->curr_token.kind == Token_EOF) {
 		    break;
@@ -2660,12 +2684,18 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) {
 		    if (field->type->kind == Ast_TypeType ||
 		        field->type->kind == Ast_PolyType) {
 				is_generic = true;
-				break;
+				goto end;
+			}
+			for_array(j, field->names) {
+				Ast *name = field->names[j];
+				if (name->kind == Ast_PolyType) {
+					is_generic = true;
+					goto end;
+				}
 			}
 		}
 	}
-
-
+end:
 	return ast_proc_type(f, proc_token, params, results, tags, cc, is_generic);
 }
 
@@ -2805,7 +2835,7 @@ struct AstAndFlags {
 	u32      flags;
 };
 
-Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> list, bool ignore_flags) {
+Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> list, bool ignore_flags, bool allow_poly_names) {
 	auto idents = array_make<Ast *>(heap_allocator(), 0, list.count);
 	// Convert to ident list
 	for_array(i, list) {
@@ -2821,6 +2851,20 @@ Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> list, bool ign
 		case Ast_Ident:
 		case Ast_BadExpr:
 			break;
+
+		case Ast_PolyType:
+			if (allow_poly_names) {
+				if (ident->PolyType.specialization == nullptr) {
+					syntax_error(ident, "Polymorphic identifiers are not yet supported");
+					break;
+				} else {
+					syntax_error(ident, "Expected a polymorphic identifier without any specialization");
+				}
+			} else {
+				syntax_error(ident, "Expected a non-polymorphic identifier");
+			}
+			/*fallthrough*/
+
 		default:
 			syntax_error(ident, "Expected an identifier");
 			ident = ast_ident(f, blank_token);
@@ -2891,6 +2935,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
 	auto list = array_make<AstAndFlags>(heap_allocator());
 	defer (array_free(&list));
 
+	bool allow_poly_names = allow_type_token;
+
 	isize total_name_count = 0;
 	bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
 	bool seen_ellipsis = false;
@@ -2915,7 +2961,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
 
 
 	if (f->curr_token.kind == Token_Colon) {
-		Array<Ast *> names = convert_to_ident_list(f, list, true); // Copy for semantic reasons
+		Array<Ast *> names = convert_to_ident_list(f, list, true, allow_poly_names); // Copy for semantic reasons
 		if (names.count == 0) {
 			syntax_error(f->curr_token, "Empty field declaration");
 		}
@@ -2971,7 +3017,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi
 			CommentGroup *docs = f->lead_comment;
 
 			u32 set_flags = parse_field_prefixes(f);
-			Array<Ast *> names = parse_ident_list(f);
+			Array<Ast *> names = parse_ident_list(f, allow_poly_names);
 			if (names.count == 0) {
 				syntax_error(f->curr_token, "Empty field declaration");
 				break;

+ 4 - 0
src/parser.hpp

@@ -418,6 +418,10 @@ AST_KIND(_DeclEnd,   "", bool) \
 		Ast *list; \
 	}) \
 AST_KIND(_TypeBegin, "", bool) \
+	AST_KIND(TypeidType, "typeid", struct { \
+		Token token; \
+		Ast *specialization; \
+	}) \
 	AST_KIND(TypeType, "type", struct { \
 		Token token; \
 		Ast *specialization; \

+ 1 - 0
src/tokenizer.cpp

@@ -86,6 +86,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
 	TOKEN_KIND(Token_foreign,     "foreign"),     \
 	TOKEN_KIND(Token_package,     "package"),     \
 	TOKEN_KIND(Token_type,        "type"),        \
+	TOKEN_KIND(Token_typeid,      "typeid"),      \
 	TOKEN_KIND(Token_when,        "when"),        \
 	TOKEN_KIND(Token_if,          "if"),          \
 	TOKEN_KIND(Token_else,        "else"),        \

+ 4 - 4
src/types.cpp

@@ -1794,17 +1794,17 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
 			// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
 			// `Raw_Any` type?
 			String data_str = str_lit("data");
-			String typeid_str = str_lit("typeid");
+			String id_str = str_lit("id");
 			gb_local_persist Entity *entity__any_data = alloc_entity_field(nullptr, make_token_ident(data_str), t_rawptr, false, 0);
-			gb_local_persist Entity *entity__any_typeid = alloc_entity_field(nullptr, make_token_ident(typeid_str), t_typeid, false, 1);
+			gb_local_persist Entity *entity__any_id = alloc_entity_field(nullptr, make_token_ident(id_str), t_typeid, false, 1);
 
 			if (field_name == data_str) {
 				selection_add_index(&sel, 0);
 				sel.entity = entity__any_data;
 				return sel;
-			} else if (field_name == typeid_str) {
+			} else if (field_name == id_str) {
 				selection_add_index(&sel, 1);
-				sel.entity = entity__any_typeid;
+				sel.entity = entity__any_id;
 				return sel;
 			}
 		#endif