Browse Source

`fmt.String_Buffer`, Fix issue #44, Tweak overloading rules

Ginger Bill 8 years ago
parent
commit
5b8be25938
11 changed files with 436 additions and 256 deletions
  1. 147 71
      core/fmt.odin
  2. 3 0
      core/os_windows.odin
  3. 43 32
      src/check_decl.c
  4. 45 20
      src/check_expr.c
  5. 130 112
      src/check_stmt.c
  6. 9 4
      src/checker.c
  7. 13 2
      src/entity.c
  8. 23 10
      src/ir.c
  9. 1 1
      src/main.c
  10. 8 4
      src/parser.c
  11. 14 0
      src/types.c

+ 147 - 71
core/fmt.odin

@@ -8,20 +8,64 @@
 
 
 _BUFFER_SIZE :: 1<<12;
 _BUFFER_SIZE :: 1<<12;
 
 
-write_string :: proc(buf: ^[]byte, s: string) {
-	append(buf, ..cast([]byte)s);
+String_Buffer :: struct {
+	is_dynamic: bool,
+	sa: []byte,
+	da: [dynamic]byte,
+};
+
+make_string_buffer_from_slice :: proc(b: []byte) -> String_Buffer {
+	return String_Buffer{
+		is_dynamic = false,
+		sa = b,
+	};
 }
 }
-write_byte :: proc(buf: ^[]byte, b: byte) {
-	append(buf, b);
+
+make_string_dynamic_buffer :: proc() -> String_Buffer {
+	return String_Buffer{
+		is_dynamic = true,
+		da = make([dynamic]byte),
+	};
+}
+string_buffer_data :: proc(buf: ^String_Buffer) -> []byte {
+	return string_buffer_data(buf^);
+}
+string_buffer_data :: proc(buf: String_Buffer) -> []byte {
+	if buf.is_dynamic {
+		return buf.da[..];
+	}
+	return buf.sa[..];
+}
+to_string :: proc(buf: String_Buffer) -> string {
+	return cast(string)string_buffer_data(buf);
+}
+
+
+write_string :: proc(buf: ^String_Buffer, s: string) {
+	write_bytes(buf, cast([]byte)s);
+}
+write_bytes :: proc(buf: ^String_Buffer, b: []byte) {
+	if buf.is_dynamic {
+		append(buf.da, ..b);
+	} else {
+		append(buf.sa, ..b);
+	}
 }
 }
-write_rune :: proc(buf: ^[]byte, r: rune) {
+write_byte :: proc(buf: ^String_Buffer, b: byte) {
+	if buf.is_dynamic {
+		append(buf.da, b);
+	} else {
+		append(buf.sa, b);
+	}
+}
+write_rune :: proc(buf: ^String_Buffer, r: rune) {
 	if r < utf8.RUNE_SELF {
 	if r < utf8.RUNE_SELF {
 		write_byte(buf, cast(byte)r);
 		write_byte(buf, cast(byte)r);
 		return;
 		return;
 	}
 	}
 
 
 	b, n := utf8.encode_rune(r);
 	b, n := utf8.encode_rune(r);
-	append(buf, ..b[0..<n]);
+	write_bytes(buf, b[0..<n]);
 }
 }
 
 
 Fmt_Info :: struct {
 Fmt_Info :: struct {
@@ -39,7 +83,7 @@ Fmt_Info :: struct {
 	reordered:      bool,
 	reordered:      bool,
 	good_arg_index: bool,
 	good_arg_index: bool,
 
 
-	buf: ^[]byte,
+	buf: ^String_Buffer,
 	arg: any, // Temporary
 	arg: any, // Temporary
 }
 }
 
 
@@ -47,25 +91,28 @@ Fmt_Info :: struct {
 
 
 fprint :: proc(fd: os.Handle, args: ..any) -> int {
 fprint :: proc(fd: os.Handle, args: ..any) -> int {
 	data: [_BUFFER_SIZE]byte;
 	data: [_BUFFER_SIZE]byte;
-	buf := data[0..<0];
-	bprint(^buf, ..args);
-	os.write(fd, buf);
-	return len(buf);
+	buf := make_string_buffer_from_slice(data[0..<0]);
+	sbprint(^buf, ..args);
+	res := string_buffer_data(buf);
+	os.write(fd, res);
+	return len(res);
 }
 }
 
 
 fprintln :: proc(fd: os.Handle, args: ..any) -> int {
 fprintln :: proc(fd: os.Handle, args: ..any) -> int {
 	data: [_BUFFER_SIZE]byte;
 	data: [_BUFFER_SIZE]byte;
-	buf := data[0..<0];
-	bprintln(^buf, ..args);
-	os.write(fd, buf);
-	return len(buf);
+	buf := make_string_buffer_from_slice(data[0..<0]);
+	sbprintln(^buf, ..args);
+	res := string_buffer_data(buf);
+	os.write(fd, res);
+	return len(res);
 }
 }
 fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
 fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
 	data: [_BUFFER_SIZE]byte;
 	data: [_BUFFER_SIZE]byte;
-	buf := data[0..<0];
-	bprintf(^buf, fmt, ..args);
-	os.write(fd, buf);
-	return len(buf);
+	buf := make_string_buffer_from_slice(data[0..<0]);
+	sbprintf(^buf, fmt, ..args);
+	res := string_buffer_data(buf);
+	os.write(fd, res);
+	return len(res);
 }
 }
 
 
 
 
@@ -80,14 +127,56 @@ printf :: proc(fmt: string, args: ..any) -> int {
 }
 }
 
 
 
 
+// aprint* procedures return a string that was allocated with the current context
+// They must be freed accordingly
+aprint :: proc(args: ..any) -> string {
+	buf := make_string_dynamic_buffer();
+	sbprint(^buf, ..args);
+	return to_string(buf);
+}
+aprintln :: proc(args: ..any) -> string {
+	buf := make_string_dynamic_buffer();
+	sbprintln(^buf, ..args);
+	return to_string(buf);
+}
+aprintf :: proc(fmt: string, args: ..any) -> string {
+	buf := make_string_dynamic_buffer();
+	sbprintf(^buf, fmt, ..args);
+	return to_string(buf);
+}
+
+
+// bprint* procedures
+
+
+// aprint* procedure return a string that was allocated with the current context
+// They must be freed accordingly
+bprint :: proc(buf: []byte, args: ..any) -> int {
+	sb := make_string_buffer_from_slice(buf[0..<0..<len(buf)]);
+	return sbprint(^sb, ..args);
+}
+bprintln :: proc(buf: []byte, args: ..any) -> int {
+	sb := make_string_buffer_from_slice(buf[0..<0..<len(buf)]);
+	return sbprintln(^sb, ..args);
+}
+bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> int {
+	sb := make_string_buffer_from_slice(buf[0..<0..<len(buf)]);
+	return sbprintf(^sb, fmt, ..args);
+}
+
+
+
+
+
+
 fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
 fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
 	data: [_BUFFER_SIZE]byte;
 	data: [_BUFFER_SIZE]byte;
-	buf := data[0..<0];
+	buf := make_string_buffer_from_slice(data[0..<0]);
 	write_type(^buf, info);
 	write_type(^buf, info);
-	os.write(fd, buf);
+	os.write(fd, string_buffer_data(buf));
 }
 }
 
 
-write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
+write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) {
 	if ti == nil {
 	if ti == nil {
 		return;
 		return;
 	}
 	}
@@ -273,51 +362,6 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
 }
 }
 
 
 
 
-bprint :: proc(buf: ^[]byte, args: ..any) -> int {
-	fi: Fmt_Info;
-	fi.buf = buf;
-
-	prev_string := false;
-	for arg, i in args {
-		is_string := arg != nil && types.is_string(arg.type_info);
-		if i > 0 && !is_string && !prev_string {
-			write_byte(buf, ' ');
-		}
-		fmt_value(^fi, args[i], 'v');
-		prev_string = is_string;
-	}
-	return len(buf);
-}
-
-bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
-	fi: Fmt_Info;
-	fi.buf = buf;
-
-	for arg, i in args {
-		if i > 0 {
-			write_byte(buf, ' ');
-		}
-		fmt_value(^fi, args[i], 'v');
-	}
-	write_byte(buf, '\n');
-	return len(buf);
-}
-
-sprint :: proc(buf: []byte, args: ..any) -> string {
-	count := bprint(^buf, ..args);
-	return cast(string)buf[0..<count];
-}
-sprintln :: proc(buf: []byte, args: ..any) -> string {
-	count := bprintln(^buf, ..args);
-	return cast(string)buf[0..<count];
-}
-sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
-	count := bprintf(^buf, fmt, ..args);
-	return cast(string)buf[0..<count];
-}
-
-
-
 
 
 _parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) {
 _parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) {
 	is_digit :: proc(r: rune) -> bool #inline {
 	is_digit :: proc(r: rune) -> bool #inline {
@@ -439,9 +483,10 @@ fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) {
 		pad_byte = '0';
 		pad_byte = '0';
 	}
 	}
 
 
-	count := min(width, cap(fi.buf)-len(fi.buf));
+	data := string_buffer_data(fi.buf^);
+	count := min(width, cap(data)-len(data));
 	for _ in 0..count {
 	for _ in 0..count {
-		append(fi.buf, pad_byte);
+		write_byte(fi.buf, pad_byte);
 	}
 	}
 }
 }
 
 
@@ -998,7 +1043,38 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 }
 }
 
 
 
 
-bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
+
+sbprint :: proc(buf: ^String_Buffer, args: ..any) -> int {
+	fi: Fmt_Info;
+	fi.buf = buf;
+
+	prev_string := false;
+	for arg, i in args {
+		is_string := arg != nil && types.is_string(arg.type_info);
+		if i > 0 && !is_string && !prev_string {
+			write_byte(buf, ' ');
+		}
+		fmt_value(^fi, args[i], 'v');
+		prev_string = is_string;
+	}
+	return len(string_buffer_data(buf));
+}
+
+sbprintln :: proc(buf: ^String_Buffer, args: ..any) -> int {
+	fi: Fmt_Info;
+	fi.buf = buf;
+
+	for arg, i in args {
+		if i > 0 {
+			write_byte(buf, ' ');
+		}
+		fmt_value(^fi, args[i], 'v');
+	}
+	write_byte(buf, '\n');
+	return len(string_buffer_data(buf));
+}
+
+sbprintf :: proc(b: ^String_Buffer, fmt: string, args: ..any) -> int {
 	fi := Fmt_Info{};
 	fi := Fmt_Info{};
 	end := len(fmt);
 	end := len(fmt);
 	arg_index := 0;
 	arg_index := 0;
@@ -1128,5 +1204,5 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
 		write_string(b, ")");
 		write_string(b, ")");
 	}
 	}
 
 
-	return len(b);
+	return len(string_buffer_data(b));
 }
 }

+ 3 - 0
core/os_windows.odin

@@ -109,6 +109,9 @@ close :: proc(fd: Handle) {
 	win32.CloseHandle(cast(win32.Handle)fd);
 	win32.CloseHandle(cast(win32.Handle)fd);
 }
 }
 
 
+write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
+	return write(fd, cast([]byte)str);
+}
 write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	if len(data) == 0 {
 	if len(data) == 0 {
 		return 0, ERROR_NONE;
 		return 0, ERROR_NONE;

+ 43 - 32
src/check_decl.c

@@ -413,7 +413,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 }
 }
 
 
 
 
-void check_alias_decl(Checker *c, Entity *e, AstNode *expr) {
+void check_alias_decl(Checker *c, Entity *e, AstNode *expr, Type *named_type) {
 	GB_ASSERT(e->type == NULL);
 	GB_ASSERT(e->type == NULL);
 	GB_ASSERT(e->kind == Entity_Alias);
 	GB_ASSERT(e->kind == Entity_Alias);
 
 
@@ -431,36 +431,47 @@ void check_alias_decl(Checker *c, Entity *e, AstNode *expr) {
 		return;
 		return;
 	}
 	}
 
 
-	if (expr->kind == AstNode_Ident) {
-		Operand o = {0};
-		Entity *f = check_ident(c, &o, expr, NULL, NULL, true);
-		if (f != NULL) {
-			e->Alias.original = f;
-			e->type = f->type;
-		}
-		return;
-	} else if (expr->kind == AstNode_SelectorExpr) {
-		Operand o = {0};
-		Entity *f = check_selector(c, &o, expr, NULL);
-		if (f != NULL) {
-			e->Alias.original = f;
-			e->type = f->type;
-		}
-		return;
-	}
-
-	Operand o = {0};
-	check_expr_or_type(c, &o, expr);
-	if (o.mode == Addressing_Invalid) {
-		return;
-	}
-	switch (o.mode) {
-	case Addressing_Type:
-		e->type = o.type;
-		break;
-	default:
+	Operand operand = {0};
+	check_expr_or_type(c, &operand, expr);
+	if (operand.mode != Addressing_Type) {
 		error_node(expr, "#alias declarations only allow types");
 		error_node(expr, "#alias declarations only allow types");
+		return;
 	}
 	}
+	e->kind = Entity_TypeName;
+	e->TypeName.is_type_alias = true;
+	e->type = NULL;
+
+	DeclInfo *d = c->context.decl;
+	d->type_expr = expr;
+	check_type_decl(c, e, d->type_expr, named_type);
+
+
+	// Operand o = {0};
+	// Entity *f = NULL;
+	// if (expr->kind == AstNode_Ident) {
+	// 	f = check_ident(c, &o, expr, NULL, NULL, true);
+	// } else if (expr->kind == AstNode_SelectorExpr) {
+	// 	f = check_selector(c, &o, expr, NULL);
+	// } else {
+	// 	check_expr_or_type(c, &o, expr);
+	// }
+	// if (o.mode == Addressing_Invalid) {
+	// 	return;
+	// }
+	// switch (o.mode) {
+	// case Addressing_Type:
+	// 	e->type = o.type;
+	// 	// e->kind = Entity_TypeName;
+	// 	// e->TypeName.is_type_alias = true;
+	// 	e->Alias.kind     = EntityAlias_Type;
+	// 	e->Alias.original = f;
+	// 	break;
+	// default:
+	// 	error_node(expr, "#alias declarations only allow types");
+	// 	e->kind = Entity_Invalid;
+	// 	e->type = t_invalid;
+	// 	break;
+	// }
 }
 }
 
 
 void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
 void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
@@ -495,12 +506,12 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
 	case Entity_TypeName:
 	case Entity_TypeName:
 		check_type_decl(c, e, d->type_expr, named_type);
 		check_type_decl(c, e, d->type_expr, named_type);
 		break;
 		break;
+	case Entity_Alias:
+		check_alias_decl(c, e, d->init_expr, named_type);
+		break;
 	case Entity_Procedure:
 	case Entity_Procedure:
 		check_proc_lit(c, e, d);
 		check_proc_lit(c, e, d);
 		break;
 		break;
-	case Entity_Alias:
-		check_alias_decl(c, e, d->init_expr);
-		break;
 	}
 	}
 
 
 	c->context = prev;
 	c->context = prev;

+ 45 - 20
src/check_expr.c

@@ -147,7 +147,31 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 		if (dst->kind == Type_Basic) {
 		if (dst->kind == Type_Basic) {
 			if (operand->mode == Addressing_Constant) {
 			if (operand->mode == Addressing_Constant) {
 				if (check_representable_as_constant(c, operand->value, dst, NULL)) {
 				if (check_representable_as_constant(c, operand->value, dst, NULL)) {
-					return 1;
+					if (is_type_typed(dst) && src->kind == Type_Basic) {
+						switch (src->Basic.kind) {
+						case Basic_UntypedInteger:
+							if (is_type_integer(dst)) {
+								return 1;
+							}
+							break;
+						case Basic_UntypedFloat:
+							if (is_type_float(dst)) {
+								return 1;
+							}
+							break;
+						case Basic_UntypedComplex:
+							if (is_type_complex(dst)) {
+								return 1;
+							}
+							break;
+						case Basic_UntypedQuaternion:
+							if (is_type_quaternion(dst)) {
+								return 1;
+							}
+							break;
+						}
+					}
+					return 2;
 				}
 				}
 				return -1;
 				return -1;
 			}
 			}
@@ -1058,12 +1082,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type) {
 			case 32: new_type = t_u32; break;
 			case 32: new_type = t_u32; break;
 			case 64: new_type = t_u64; break;
 			case 64: new_type = t_u64; break;
 			default:
 			default:
-				// NOTE(bill): It could be an empty struct that is passed
-				// and if that is the case, no need to pass by pointer
-				// (I think..)
-				if (size > 0) {
-					new_type = make_type_pointer(a, original_type);
-				}
+				new_type = make_type_pointer(a, original_type);
 				break;
 				break;
 			}
 			}
 		} break;
 		} break;
@@ -1196,11 +1215,6 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
 
 
 	e->flags |= EntityFlag_Used;
 	e->flags |= EntityFlag_Used;
 
 
-	Entity *original_e = e;
-	while (e != NULL && e->kind == Entity_Alias && e->Alias.original != NULL) {
-		e = e->Alias.original;
-	}
-
 	Type *type = e->type;
 	Type *type = e->type;
 	switch (e->kind) {
 	switch (e->kind) {
 	case Entity_Constant:
 	case Entity_Constant:
@@ -1261,6 +1275,17 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
 		o->mode = Addressing_Value;
 		o->mode = Addressing_Value;
 		break;
 		break;
 
 
+	case Entity_Alias: {
+		// error_node(n, "#alias entities are not yet supported");
+		// TODO(bill): Fix Entity_Alias rules
+		if (e->Alias.kind == EntityAlias_Type) {
+			o->mode = Addressing_Type;
+		} else {
+			o->mode = Addressing_Invalid;
+			return e;
+		}
+	} break;
+
 	default:
 	default:
 		compiler_error("Unknown EntityKind");
 		compiler_error("Unknown EntityKind");
 		break;
 		break;
@@ -3136,6 +3161,11 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 		operand->builtin_id = entity->Builtin.id;
 		operand->builtin_id = entity->Builtin.id;
 		break;
 		break;
 
 
+	case Entity_Alias: {
+		error_node(selector, "#alias entities are not yet supported");
+		return NULL;
+	} break;
+
 	// NOTE(bill): These cases should never be hit but are here for sanity reasons
 	// NOTE(bill): These cases should never be hit but are here for sanity reasons
 	case Entity_Nil:
 	case Entity_Nil:
 		operand->mode = Addressing_Value;
 		operand->mode = Addressing_Value;
@@ -3899,8 +3929,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		Operand z = {0};
 		Operand z = {0};
 		Operand w = {0};
 		Operand w = {0};
 
 
-		GB_PANIC("BuiltinProc_quaternion");
-
 		// NOTE(bill): Invalid will be the default till fixed
 		// NOTE(bill): Invalid will be the default till fixed
 		operand->type = t_invalid;
 		operand->type = t_invalid;
 		operand->mode = Addressing_Invalid;
 		operand->mode = Addressing_Invalid;
@@ -3990,12 +4018,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 
 		BasicKind kind = core_type(x.type)->Basic.kind;
 		BasicKind kind = core_type(x.type)->Basic.kind;
 		switch (kind) {
 		switch (kind) {
-		case Basic_complex64:         x.type = t_f32;           break;
-		case Basic_complex128:        x.type = t_f64;           break;
-		case Basic_UntypedComplex:    x.type = t_untyped_float; break;
-		case Basic_quaternion128:     x.type = t_f32;           break;
-		case Basic_quaternion256:     x.type = t_f64;           break;
-		case Basic_UntypedQuaternion: x.type = t_untyped_float; break;
+		case Basic_f32:          operand->type = t_quaternion128;      break;
+		case Basic_f64:          operand->type = t_quaternion256;      break;
+		case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break;
 		default: GB_PANIC("Invalid type"); break;
 		default: GB_PANIC("Invalid type"); break;
 		}
 		}
 	} break;
 	} break;

+ 130 - 112
src/check_stmt.c

@@ -432,6 +432,134 @@ void check_label(Checker *c, AstNode *label) {
 	}
 	}
 }
 }
 
 
+// Returns `true` for `continue`, `false` for `return`
+bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bool is_selector, Entity *e) {
+	if (e == NULL) {
+		error(us->token, "`using` applied to an unknown entity");
+		return true;
+	}
+
+	switch (e->kind) {
+	case Entity_Alias: {
+		if (e->Alias.original != NULL) {
+			check_using_stmt_entity(c, us, expr, is_selector, e->Alias.original);
+		} else {
+			error(us->token, "`using` cannot be applied to the alias `%.*s`", LIT(e->token.string));
+			return false;
+		}
+	} break;
+
+	case Entity_TypeName: {
+		Type *t = base_type(e->type);
+		if (is_type_union(t)) {
+			TokenPos pos = ast_node_token(expr).pos;
+			for (isize i = 1; i < t->Record.variant_count; i++) {
+				Entity *f = t->Record.variants[i];
+				// gb_printf_err("%s\n", type_to_string(f->type));
+				Entity *found = scope_insert_entity(c->context.scope, f);
+				if (found != NULL) {
+					gbString expr_str = expr_to_string(expr);
+					error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+					gb_string_free(expr_str);
+					return false;
+				}
+				f->using_parent = e;
+			}
+		} else if (is_type_enum(t)) {
+			for (isize i = 0; i < t->Record.field_count; i++) {
+				Entity *f = t->Record.fields[i];
+				Entity *found = scope_insert_entity(c->context.scope, f);
+				if (found != NULL) {
+					gbString expr_str = expr_to_string(expr);
+					error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+					gb_string_free(expr_str);
+					return false;
+				}
+				f->using_parent = e;
+			}
+
+		} else {
+			error(us->token, "`using` can be only applied to `union` or `enum` type entities");
+		}
+	} break;
+
+	case Entity_ImportName: {
+		Scope *scope = e->ImportName.scope;
+		for_array(i, scope->elements.entries) {
+			Entity *decl = scope->elements.entries.e[i].value;
+			Entity *found = scope_insert_entity(c->context.scope, decl);
+			if (found != NULL) {
+				gbString expr_str = expr_to_string(expr);
+				error(us->token,
+				      "Namespace collision while `using` `%s` of: %.*s\n"
+				      "\tat %.*s(%td:%td)\n"
+				      "\tat %.*s(%td:%td)",
+				      expr_str, LIT(found->token.string),
+				      LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
+				      LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
+				      );
+				gb_string_free(expr_str);
+				return false;
+			}
+		}
+	} break;
+
+	case Entity_Variable: {
+		Type *t = base_type(type_deref(e->type));
+		if (is_type_struct(t) || is_type_raw_union(t)) {
+			// TODO(bill): Make it work for unions too
+			Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
+			GB_ASSERT(found != NULL);
+			for_array(i, (*found)->elements.entries) {
+				Entity *f = (*found)->elements.entries.e[i].value;
+				if (f->kind == Entity_Variable) {
+					Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
+					if (is_selector) {
+						uvar->using_expr = expr;
+					}
+					Entity *prev = scope_insert_entity(c->context.scope, uvar);
+					if (prev != NULL) {
+						gbString expr_str = expr_to_string(expr);
+						error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
+						gb_string_free(expr_str);
+						return false;
+					}
+				}
+			}
+		} else {
+			error(us->token, "`using` can only be applied to variables of type struct or raw_union");
+			return false;
+		}
+	} break;
+
+	case Entity_Constant:
+		error(us->token, "`using` cannot be applied to a constant");
+		break;
+
+	case Entity_Procedure:
+	case Entity_Builtin:
+		error(us->token, "`using` cannot be applied to a procedure");
+		break;
+
+	case Entity_Nil:
+		error(us->token, "`using` cannot be applied to `nil`");
+		break;
+
+	case Entity_Label:
+		error(us->token, "`using` cannot be applied to a label");
+		break;
+
+	case Entity_Invalid:
+		error(us->token, "`using` cannot be applied to an invalid entity");
+		break;
+
+	default:
+		GB_PANIC("TODO(bill): `using` other expressions?");
+	}
+
+	return true;
+}
+
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
 	switch (node->kind) {
@@ -1341,118 +1469,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				continue;
 				continue;
 			}
 			}
 
 
-			if (e == NULL) {
-				error(us->token, "`using` applied to an unknown entity");
-				continue;
-			}
-
-			switch (e->kind) {
-			case Entity_TypeName: {
-				Type *t = base_type(e->type);
-				if (is_type_union(t)) {
-					TokenPos pos = ast_node_token(expr).pos;
-					for (isize i = 1; i < t->Record.variant_count; i++) {
-						Entity *f = t->Record.variants[i];
-						// gb_printf_err("%s\n", type_to_string(f->type));
-						Entity *found = scope_insert_entity(c->context.scope, f);
-						if (found != NULL) {
-							gbString expr_str = expr_to_string(expr);
-							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
-							gb_string_free(expr_str);
-							return;
-						}
-						f->using_parent = e;
-					}
-				} else if (is_type_enum(t)) {
-					for (isize i = 0; i < t->Record.field_count; i++) {
-						Entity *f = t->Record.fields[i];
-						Entity *found = scope_insert_entity(c->context.scope, f);
-						if (found != NULL) {
-							gbString expr_str = expr_to_string(expr);
-							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
-							gb_string_free(expr_str);
-							return;
-						}
-						f->using_parent = e;
-					}
-
-				} else {
-					error(us->token, "`using` can be only applied to `union` or `enum` type entities");
-				}
-			} break;
-
-			case Entity_ImportName: {
-				Scope *scope = e->ImportName.scope;
-				for_array(i, scope->elements.entries) {
-					Entity *decl = scope->elements.entries.e[i].value;
-					Entity *found = scope_insert_entity(c->context.scope, decl);
-					if (found != NULL) {
-						gbString expr_str = expr_to_string(expr);
-						error(us->token,
-						      "Namespace collision while `using` `%s` of: %.*s\n"
-						      "\tat %.*s(%td:%td)\n"
-						      "\tat %.*s(%td:%td)",
-						      expr_str, LIT(found->token.string),
-						      LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
-						      LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
-						      );
-						gb_string_free(expr_str);
-						return;
-					}
-				}
-			} break;
-
-			case Entity_Variable: {
-				Type *t = base_type(type_deref(e->type));
-				if (is_type_struct(t) || is_type_raw_union(t)) {
-					// TODO(bill): Make it work for unions too
-					Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
-					GB_ASSERT(found != NULL);
-					for_array(i, (*found)->elements.entries) {
-						Entity *f = (*found)->elements.entries.e[i].value;
-						if (f->kind == Entity_Variable) {
-							Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
-							if (is_selector) {
-								uvar->using_expr = expr;
-							}
-							Entity *prev = scope_insert_entity(c->context.scope, uvar);
-							if (prev != NULL) {
-								gbString expr_str = expr_to_string(expr);
-								error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
-								gb_string_free(expr_str);
-								return;
-							}
-						}
-					}
-				} else {
-					error(us->token, "`using` can only be applied to variables of type struct or raw_union");
-					return;
-				}
-			} break;
-
-			case Entity_Constant:
-				error(us->token, "`using` cannot be applied to a constant");
-				break;
-
-			case Entity_Procedure:
-			case Entity_Builtin:
-				error(us->token, "`using` cannot be applied to a procedure");
-				break;
-
-			case Entity_Nil:
-				error(us->token, "`using` cannot be applied to `nil`");
-				break;
-
-			case Entity_Label:
-				error(us->token, "`using` cannot be applied to a label");
-				break;
-
-			case Entity_Invalid:
-				error(us->token, "`using` cannot be applied to an invalid entity");
-				break;
-
-			default:
-				GB_PANIC("TODO(bill): `using` other expressions?");
+			if (!check_using_stmt_entity(c, us, expr, is_selector, e)) {
+				return;
 			}
 			}
 		}
 		}
 	case_end;
 	case_end;

+ 9 - 4
src/checker.c

@@ -1531,11 +1531,15 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
 						d->type_expr = type;
 						d->type_expr = type;
 						d->init_expr = type;
 						d->init_expr = type;
 					} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
 					} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
+					#if 1
 						error_node(up_init, "#alias declarations are not yet supported");
 						error_node(up_init, "#alias declarations are not yet supported");
 						continue;
 						continue;
-						// e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, NULL);
-						// d->init_expr = init->Alias.expr;
-					}else if (init != NULL && up_init->kind == AstNode_ProcLit) {
+					#else
+						e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, EntityAlias_Invalid, NULL);
+						d->type_expr = vd->type;
+						d->init_expr = up_init->Alias.expr;
+					#endif
+					} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
 						e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
 						e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
 						d->proc_lit = up_init;
 						d->proc_lit = up_init;
 						d->type_expr = vd->type;
 						d->type_expr = vd->type;
@@ -1989,7 +1993,8 @@ void check_parsed_files(Checker *c) {
 	// NOTE(bill): Check for illegal cyclic type declarations
 	// NOTE(bill): Check for illegal cyclic type declarations
 	for_array(i, c->info.definitions.entries) {
 	for_array(i, c->info.definitions.entries) {
 		Entity *e = c->info.definitions.entries.e[i].value;
 		Entity *e = c->info.definitions.entries.e[i].value;
-		if (e->kind == Entity_TypeName) {
+		if (e->kind == Entity_TypeName ||
+		    (e->kind == Entity_Alias && e->Alias.kind == EntityAlias_Type)) {
 			if (e->type != NULL) {
 			if (e->type != NULL) {
 				// i64 size  = type_size_of(c->sizes, c->allocator, e->type);
 				// i64 size  = type_size_of(c->sizes, c->allocator, e->type);
 				i64 align = type_align_of(c->allocator, e->type);
 				i64 align = type_align_of(c->allocator, e->type);

+ 13 - 2
src/entity.c

@@ -50,11 +50,18 @@ typedef enum OverloadKind {
 	Overload_Yes,
 	Overload_Yes,
 } OverloadKind;
 } OverloadKind;
 
 
+typedef	enum EntityAliasKind {
+	EntityAlias_Invalid,
+	EntityAlias_Type,
+	EntityAlias_Entity,
+} EntityAliasKind;
+
 
 
 // An Entity is a named "thing" in the language
 // An Entity is a named "thing" in the language
 typedef struct Entity Entity;
 typedef struct Entity Entity;
 struct Entity {
 struct Entity {
 	EntityKind kind;
 	EntityKind kind;
+	u64        id;
 	u32        flags;
 	u32        flags;
 	Token      token;
 	Token      token;
 	Scope *    scope;
 	Scope *    scope;
@@ -101,7 +108,8 @@ struct Entity {
 			bool   used;
 			bool   used;
 		} LibraryName;
 		} LibraryName;
 		struct {
 		struct {
-			Entity *original;
+			EntityAliasKind kind;
+			Entity *        original;
 		} Alias;
 		} Alias;
 		i32 Nil;
 		i32 Nil;
 		struct {
 		struct {
@@ -138,6 +146,7 @@ bool is_entity_exported(Entity *e) {
 	return name.text[0] != '_';
 	return name.text[0] != '_';
 }
 }
 
 
+gb_global u64 global_entity_id = 0;
 
 
 Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
 Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
 	Entity *entity = gb_alloc_item(a, Entity);
 	Entity *entity = gb_alloc_item(a, Entity);
@@ -145,6 +154,7 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
 	entity->scope  = scope;
 	entity->scope  = scope;
 	entity->token  = token;
 	entity->token  = token;
 	entity->type   = type;
 	entity->type   = type;
+	entity->id     = ++global_entity_id;
 	return entity;
 	return entity;
 }
 }
 
 
@@ -231,8 +241,9 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
 }
 }
 
 
 Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type,
 Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type,
-                          Entity *original) {
+                          EntityAliasKind kind, Entity *original) {
 	Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
 	Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
+	entity->Alias.kind     = kind;
 	entity->Alias.original = original;
 	entity->Alias.original = original;
 	return entity;
 	return entity;
 }
 }

+ 23 - 10
src/ir.c

@@ -3284,23 +3284,34 @@ String ir_mangle_name(irGen *s, String path, Entity *e) {
 	char const *ext = gb_path_extension(base);
 	char const *ext = gb_path_extension(base);
 	isize base_len = ext-1-base;
 	isize base_len = ext-1-base;
 
 
-	isize max_len = base_len + 1 + 10 + 1 + name.len;
+	isize max_len = base_len + 1 + 1 + 10 + 1 + name.len;
 	bool is_overloaded = check_is_entity_overloaded(e);
 	bool is_overloaded = check_is_entity_overloaded(e);
 	if (is_overloaded) {
 	if (is_overloaded) {
 		max_len += 21;
 		max_len += 21;
 	}
 	}
 
 
 	u8 *new_name = gb_alloc_array(a, u8, max_len);
 	u8 *new_name = gb_alloc_array(a, u8, max_len);
-	isize new_name_len = gb_snprintf(
-		cast(char *)new_name, max_len,
-		"%.*s-%u.%.*s",
-		cast(int)base_len, base,
-		file->id,
-		LIT(name));
+	isize new_name_len = 0;
+	if ((base_len > 0 && gb_char_is_digit(base[0])) ||
+	    base_len == 0) {
+		new_name_len = gb_snprintf(
+			cast(char *)new_name, max_len,
+			"_%.*s-%u.%.*s",
+			cast(int)base_len, base,
+			file->id,
+			LIT(name));
+	} else {
+		new_name_len = gb_snprintf(
+			cast(char *)new_name, max_len,
+			"%.*s-%u.%.*s",
+			cast(int)base_len, base,
+			file->id,
+			LIT(name));
+	}
 	if (is_overloaded) {
 	if (is_overloaded) {
 		char *str = cast(char *)new_name + new_name_len-1;
 		char *str = cast(char *)new_name + new_name_len-1;
 		isize len = max_len-new_name_len;
 		isize len = max_len-new_name_len;
-		isize extra = gb_snprintf(str, len, "-%tu", cast(usize)cast(uintptr)e);
+		isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
 		new_name_len += extra-1;
 		new_name_len += extra-1;
 	}
 	}
 
 
@@ -4311,7 +4322,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 					irValue *kmag = ir_build_expr(proc, ce->args.e[3]);
 					irValue *kmag = ir_build_expr(proc, ce->args.e[3]);
 					irValue *dst = ir_add_local_generated(proc, tv->type);
 					irValue *dst = ir_add_local_generated(proc, tv->type);
 
 
-					Type *ft = base_complex_elem_type(tv->type);
+					Type *ft = base_quaternion_elem_type(tv->type);
 					real = ir_emit_conv(proc, real, ft);
 					real = ir_emit_conv(proc, real, ft);
 					imag = ir_emit_conv(proc, imag, ft);
 					imag = ir_emit_conv(proc, imag, ft);
 					jmag = ir_emit_conv(proc, jmag, ft);
 					jmag = ir_emit_conv(proc, jmag, ft);
@@ -7217,6 +7228,7 @@ void ir_gen_tree(irGen *s) {
 			if (var->init != NULL && var->init->kind == irValue_Constant) {
 			if (var->init != NULL && var->init->kind == irValue_Constant) {
 				Type *t = type_deref(ir_type(var->var));
 				Type *t = type_deref(ir_type(var->var));
 				if (is_type_any(t)) {
 				if (is_type_any(t)) {
+					// NOTE(bill): Edge case for `any` type
 					Type *var_type = default_type(ir_type(var->init));
 					Type *var_type = default_type(ir_type(var->init));
 					irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
 					irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
 					irValue *data = ir_emit_struct_ep(proc, var->var, 0);
 					irValue *data = ir_emit_struct_ep(proc, var->var, 0);
@@ -7233,7 +7245,8 @@ void ir_gen_tree(irGen *s) {
 			irGlobalVariable *var = &global_variables.e[i];
 			irGlobalVariable *var = &global_variables.e[i];
 			if (var->init != NULL && var->init->kind != irValue_Constant) {
 			if (var->init != NULL && var->init->kind != irValue_Constant) {
 				Type *t = type_deref(ir_type(var->var));
 				Type *t = type_deref(ir_type(var->var));
-					if (is_type_any(t)) {
+				if (is_type_any(t)) {
+					// NOTE(bill): Edge case for `any` type
 					Type *var_type = default_type(ir_type(var->init));
 					Type *var_type = default_type(ir_type(var->init));
 					irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
 					irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
 					ir_emit_store(proc, g, var->init);
 					ir_emit_store(proc, g, var->init);

+ 1 - 1
src/main.c

@@ -430,7 +430,7 @@ int main(int argc, char **argv) {
 
 
 	exit_code = system_exec_command_line_app("ld-link", true,
 	exit_code = system_exec_command_line_app("ld-link", true,
 		"%s \"%.*s\".o -o \"%.*s%s\" %s "
 		"%s \"%.*s\".o -o \"%.*s%s\" %s "
-		"-lc "
+		"-lc -lm "
 		" %.*s "
 		" %.*s "
 		" %s "
 		" %s "
 		#if defined(GB_SYSTEM_OSX)
 		#if defined(GB_SYSTEM_OSX)

+ 8 - 4
src/parser.c

@@ -33,6 +33,7 @@ typedef struct AstFile {
 	// NOTE(bill): Used to prevent type literals in control clauses
 	// NOTE(bill): Used to prevent type literals in control clauses
 	isize          expr_level;
 	isize          expr_level;
 	bool           allow_range;
 	bool           allow_range;
+	bool           ignore_operand;
 
 
 	AstNodeArray   decls;
 	AstNodeArray   decls;
 	bool           is_global_scope;
 	bool           is_global_scope;
@@ -1849,10 +1850,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 	}
 	}
 	}
 	}
 
 
-	Token begin = f->curr_token;
-	syntax_error(begin, "Expected an operand");
-	fix_advance_to_next_stmt(f);
-	return ast_bad_expr(f, begin, f->curr_token);
+	return NULL;
 }
 }
 
 
 bool is_literal_type(AstNode *node) {
 bool is_literal_type(AstNode *node) {
@@ -1937,6 +1935,12 @@ AstNode *parse_macro_call_expr(AstFile *f, AstNode *operand) {
 
 
 AstNode *parse_atom_expr(AstFile *f, bool lhs) {
 AstNode *parse_atom_expr(AstFile *f, bool lhs) {
 	AstNode *operand = parse_operand(f, lhs);
 	AstNode *operand = parse_operand(f, lhs);
+	if (operand == NULL) {
+		Token begin = f->curr_token;
+		syntax_error(begin, "Expected an operand");
+		fix_advance_to_next_stmt(f);
+		operand = ast_bad_expr(f, begin, f->curr_token);
+	}
 
 
 	bool loop = true;
 	bool loop = true;
 	while (loop) {
 	while (loop) {

+ 14 - 0
src/types.c

@@ -551,6 +551,20 @@ bool is_type_named(Type *t) {
 	}
 	}
 	return t->kind == Type_Named;
 	return t->kind == Type_Named;
 }
 }
+bool is_type_named_alias(Type *t) {
+	if (!is_type_named(t)) {
+		return false;
+	}
+	Entity *e = t->Named.type_name;
+	if (e == NULL) {
+		return false;
+	}
+	if (e->kind != Entity_TypeName) {
+		return false;
+	}
+	return e->TypeName.is_type_alias;
+}
+
 bool is_type_boolean(Type *t) {
 bool is_type_boolean(Type *t) {
 	t = core_type(t);
 	t = core_type(t);
 	if (t->kind == Type_Basic) {
 	if (t->kind == Type_Basic) {