mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-21 05:05:00 -07:00
fmt.String_Buffer, Fix issue #44, Tweak overloading rules
This commit is contained in:
+150
-74
@@ -8,20 +8,64 @@
|
||||
|
||||
_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),
|
||||
};
|
||||
}
|
||||
write_rune :: proc(buf: ^[]byte, r: rune) {
|
||||
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_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 {
|
||||
write_byte(buf, cast(byte)r);
|
||||
return;
|
||||
}
|
||||
|
||||
b, n := utf8.encode_rune(r);
|
||||
append(buf, ..b[0..<n]);
|
||||
write_bytes(buf, b[0..<n]);
|
||||
}
|
||||
|
||||
Fmt_Info :: struct {
|
||||
@@ -39,7 +83,7 @@ Fmt_Info :: struct {
|
||||
reordered: bool,
|
||||
good_arg_index: bool,
|
||||
|
||||
buf: ^[]byte,
|
||||
buf: ^String_Buffer,
|
||||
arg: any, // Temporary
|
||||
}
|
||||
|
||||
@@ -47,25 +91,28 @@ Fmt_Info :: struct {
|
||||
|
||||
fprint :: proc(fd: os.Handle, args: ..any) -> int {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
|
||||
fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
|
||||
data: [_BUFFER_SIZE]byte;
|
||||
buf := data[0..<0];
|
||||
write_type(^buf, info);
|
||||
os.write(fd, buf);
|
||||
// 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);
|
||||
}
|
||||
|
||||
write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
|
||||
|
||||
// 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) {
|
||||
data: [_BUFFER_SIZE]byte;
|
||||
buf := make_string_buffer_from_slice(data[0..<0]);
|
||||
write_type(^buf, info);
|
||||
os.write(fd, string_buffer_data(buf));
|
||||
}
|
||||
|
||||
write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) {
|
||||
if ti == nil {
|
||||
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) {
|
||||
is_digit :: proc(r: rune) -> bool #inline {
|
||||
@@ -439,9 +483,10 @@ fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) {
|
||||
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 {
|
||||
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{};
|
||||
end := len(fmt);
|
||||
arg_index := 0;
|
||||
@@ -1128,5 +1204,5 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
|
||||
write_string(b, ")");
|
||||
}
|
||||
|
||||
return len(b);
|
||||
return len(string_buffer_data(b));
|
||||
}
|
||||
|
||||
@@ -109,6 +109,9 @@ close :: proc(fd: Handle) {
|
||||
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) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
|
||||
+43
-32
@@ -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->kind == Entity_Alias);
|
||||
|
||||
@@ -431,36 +431,47 @@ void check_alias_decl(Checker *c, Entity *e, AstNode *expr) {
|
||||
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");
|
||||
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) {
|
||||
@@ -495,12 +506,12 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
break;
|
||||
case Entity_Alias:
|
||||
check_alias_decl(c, e, d->init_expr, named_type);
|
||||
break;
|
||||
case Entity_Procedure:
|
||||
check_proc_lit(c, e, d);
|
||||
break;
|
||||
case Entity_Alias:
|
||||
check_alias_decl(c, e, d->init_expr);
|
||||
break;
|
||||
}
|
||||
|
||||
c->context = prev;
|
||||
|
||||
+45
-20
@@ -147,7 +147,31 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
||||
if (dst->kind == Type_Basic) {
|
||||
if (operand->mode == Addressing_Constant) {
|
||||
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;
|
||||
}
|
||||
@@ -1058,12 +1082,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type) {
|
||||
case 32: new_type = t_u32; break;
|
||||
case 64: new_type = t_u64; break;
|
||||
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;
|
||||
@@ -1196,11 +1215,6 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
|
||||
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;
|
||||
switch (e->kind) {
|
||||
case Entity_Constant:
|
||||
@@ -1261,6 +1275,17 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
o->mode = Addressing_Value;
|
||||
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:
|
||||
compiler_error("Unknown EntityKind");
|
||||
break;
|
||||
@@ -3136,6 +3161,11 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
operand->builtin_id = entity->Builtin.id;
|
||||
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
|
||||
case Entity_Nil:
|
||||
operand->mode = Addressing_Value;
|
||||
@@ -3899,8 +3929,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
Operand z = {0};
|
||||
Operand w = {0};
|
||||
|
||||
GB_PANIC("BuiltinProc_quaternion");
|
||||
|
||||
// NOTE(bill): Invalid will be the default till fixed
|
||||
operand->type = t_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;
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
|
||||
+130
-112
@@ -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) {
|
||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||
switch (node->kind) {
|
||||
@@ -1341,118 +1469,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
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;
|
||||
|
||||
+9
-4
@@ -1531,11 +1531,15 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
|
||||
d->type_expr = type;
|
||||
d->init_expr = type;
|
||||
} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
|
||||
#if 1
|
||||
error_node(up_init, "#alias declarations are not yet supported");
|
||||
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);
|
||||
d->proc_lit = up_init;
|
||||
d->type_expr = vd->type;
|
||||
@@ -1989,7 +1993,8 @@ void check_parsed_files(Checker *c) {
|
||||
// NOTE(bill): Check for illegal cyclic type declarations
|
||||
for_array(i, c->info.definitions.entries) {
|
||||
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) {
|
||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||
i64 align = type_align_of(c->allocator, e->type);
|
||||
|
||||
+13
-2
@@ -50,11 +50,18 @@ typedef enum OverloadKind {
|
||||
Overload_Yes,
|
||||
} OverloadKind;
|
||||
|
||||
typedef enum EntityAliasKind {
|
||||
EntityAlias_Invalid,
|
||||
EntityAlias_Type,
|
||||
EntityAlias_Entity,
|
||||
} EntityAliasKind;
|
||||
|
||||
|
||||
// An Entity is a named "thing" in the language
|
||||
typedef struct Entity Entity;
|
||||
struct Entity {
|
||||
EntityKind kind;
|
||||
u64 id;
|
||||
u32 flags;
|
||||
Token token;
|
||||
Scope * scope;
|
||||
@@ -101,7 +108,8 @@ struct Entity {
|
||||
bool used;
|
||||
} LibraryName;
|
||||
struct {
|
||||
Entity *original;
|
||||
EntityAliasKind kind;
|
||||
Entity * original;
|
||||
} Alias;
|
||||
i32 Nil;
|
||||
struct {
|
||||
@@ -138,6 +146,7 @@ bool is_entity_exported(Entity *e) {
|
||||
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 *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->token = token;
|
||||
entity->type = type;
|
||||
entity->id = ++global_entity_id;
|
||||
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 *original) {
|
||||
EntityAliasKind kind, Entity *original) {
|
||||
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
|
||||
entity->Alias.kind = kind;
|
||||
entity->Alias.original = original;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -3284,23 +3284,34 @@ String ir_mangle_name(irGen *s, String path, Entity *e) {
|
||||
char const *ext = gb_path_extension(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);
|
||||
if (is_overloaded) {
|
||||
max_len += 21;
|
||||
}
|
||||
|
||||
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) {
|
||||
char *str = cast(char *)new_name + new_name_len-1;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4311,7 +4322,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
|
||||
irValue *kmag = ir_build_expr(proc, ce->args.e[3]);
|
||||
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);
|
||||
imag = ir_emit_conv(proc, imag, 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) {
|
||||
Type *t = type_deref(ir_type(var->var));
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for `any` type
|
||||
Type *var_type = default_type(ir_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);
|
||||
@@ -7233,7 +7245,8 @@ void ir_gen_tree(irGen *s) {
|
||||
irGlobalVariable *var = &global_variables.e[i];
|
||||
if (var->init != NULL && var->init->kind != irValue_Constant) {
|
||||
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));
|
||||
irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
|
||||
ir_emit_store(proc, g, var->init);
|
||||
|
||||
+1
-1
@@ -430,7 +430,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
exit_code = system_exec_command_line_app("ld-link", true,
|
||||
"%s \"%.*s\".o -o \"%.*s%s\" %s "
|
||||
"-lc "
|
||||
"-lc -lm "
|
||||
" %.*s "
|
||||
" %s "
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
|
||||
+8
-4
@@ -33,6 +33,7 @@ typedef struct AstFile {
|
||||
// NOTE(bill): Used to prevent type literals in control clauses
|
||||
isize expr_level;
|
||||
bool allow_range;
|
||||
bool ignore_operand;
|
||||
|
||||
AstNodeArray decls;
|
||||
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) {
|
||||
@@ -1937,6 +1935,12 @@ AstNode *parse_macro_call_expr(AstFile *f, AstNode *operand) {
|
||||
|
||||
AstNode *parse_atom_expr(AstFile *f, bool 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;
|
||||
while (loop) {
|
||||
|
||||
+14
@@ -551,6 +551,20 @@ bool is_type_named(Type *t) {
|
||||
}
|
||||
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) {
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
|
||||
Reference in New Issue
Block a user