diff --git a/code/demo.odin b/code/demo.odin index 4d87bbfec..912168306 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,6 +1,11 @@ import "fmt.odin"; +import "strconv.odin"; + +Opaque :: union{}; main :: proc() { - fmt.println("Hellope!"); + buf := make([]u8, 0, 10); + s := strconv.append_bool(buf, true); + fmt.println(s); } diff --git a/core/_preload.odin b/core/_preload.odin index af0a9270d..316b5b2f1 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -314,7 +314,6 @@ copy :: proc(dst, src: $T/[]$E) -> int #cc_contextless { } - append :: proc(array: ^$T/[]$E, args: ...E) -> int #cc_contextless { if array == nil do return 0; @@ -356,6 +355,19 @@ append :: proc(array: ^$T/[dynamic]$E, args: ...E) -> int { return len(array); } +append :: proc(array: ^$T/[]u8, args: ...string) -> int { + for arg in args { + append(array, ...cast([]u8)arg); + } + return len(array); +} +append :: proc(array: ^$T/[dynamic]u8, args: ...string) -> int { + for arg in args { + append(array, ...cast([]u8)arg); + } + return len(array); +} + pop :: proc(array: ^$T/[]$E) -> E #cc_contextless { if array == nil do return E{}; assert(len(array) > 0); diff --git a/core/fmt.odin b/core/fmt.odin index 0c0ecf4f4..c8723f6c4 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -186,7 +186,8 @@ write_type :: proc(buf: ^StringBuffer, ti: ^TypeInfo) { case ti == type_info_of(int): write_string(buf, "int"); case ti == type_info_of(uint): write_string(buf, "uint"); case: - write_string(buf, info.signed ? "i" : "u"); + if info.signed do write_byte(buf, 'i'); + else do write_byte(buf, 'u'); write_int(buf, i64(8*ti.size), 10); } case Rune: @@ -424,7 +425,9 @@ fmt_bad_verb :: proc(using fi: ^FmtInfo, verb: rune) { fmt_bool :: proc(using fi: ^FmtInfo, b: bool, verb: rune) { match verb { case 't', 'v': - write_string(buf, b ? "true" : "false"); + s := "false"; + if b do s = "true"; + write_string(buf, s); case: fmt_bad_verb(fi, verb); } @@ -434,7 +437,8 @@ fmt_bool :: proc(using fi: ^FmtInfo, b: bool, verb: rune) { fmt_write_padding :: proc(fi: ^FmtInfo, width: int) { if width <= 0 do return; - pad_byte: u8 = fi.space ? ' ' : '0'; + pad_byte: u8 = '0'; + if fi.space do pad_byte = ' '; for _ in 0..width { write_byte(fi.buf, pad_byte); @@ -569,7 +573,8 @@ fmt_float :: proc(fi: ^FmtInfo, v: f64, bit_size: int, verb: rune) { // case 'f', 'F', 'v': case 'f', 'F', 'v': - prec: int = fi.prec_set ? fi.prec : 3; + prec: int = 3; + if fi.prec_set do prec = fi.prec; buf: [386]u8; str := strconv.append_float(buf[1..1], v, 'f', prec, bit_size); @@ -617,7 +622,9 @@ fmt_string :: proc(fi: ^FmtInfo, s: string, verb: rune) { for i in 0..len(s) { if i > 0 && space do write_byte(fi.buf, ' '); - _fmt_int(fi, u128(s[i]), 16, false, 8, verb == 'x' ? __DIGITS_LOWER : __DIGITS_UPPER); + char_set := __DIGITS_UPPER; + if verb == 'x' do char_set = __DIGITS_LOWER; + _fmt_int(fi, u128(s[i]), 16, false, 8, char_set); } case: diff --git a/core/math.odin b/core/math.odin index bf85b5026..04af9593a 100644 --- a/core/math.odin +++ b/core/math.odin @@ -54,8 +54,8 @@ unlerp :: proc(a, b, x: f32) -> (t: f32) do return (x-a)/(b-a); unlerp :: proc(a, b, x: f64) -> (t: f64) do return (x-a)/(b-a); -sign :: proc(x: f32) -> f32 do return x >= 0 ? +1 : -1; -sign :: proc(x: f64) -> f64 do return x >= 0 ? +1 : -1; +sign :: proc(x: f32) -> f32 { if x >= 0 do return +1; return -1; } +sign :: proc(x: f64) -> f64 { if x >= 0 do return +1; return -1; } @@ -75,14 +75,14 @@ copy_sign :: proc(x, y: f64) -> f64 { return transmute(f64, ix); } -round :: proc(x: f32) -> f32 do return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); -round :: proc(x: f64) -> f64 do return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); +round :: proc(x: f32) -> f32 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); } +round :: proc(x: f64) -> f64 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); } -floor :: proc(x: f32) -> f32 do return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); // TODO: Get accurate versions -floor :: proc(x: f64) -> f64 do return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); // TODO: Get accurate versions +floor :: proc(x: f32) -> f32 { if x >= 0 do f32(i64(x)) return f32(i64(x-0.5)); } // TODO: Get accurate versions +floor :: proc(x: f64) -> f64 { if x >= 0 do f64(i64(x)) return f64(i64(x-0.5)); } // TODO: Get accurate versions -ceil :: proc(x: f32) -> f32 do return x < 0 ? f32(i64(x)) : f32(i64(x+1)); // TODO: Get accurate versions -ceil :: proc(x: f64) -> f64 do return x < 0 ? f64(i64(x)) : f64(i64(x+1)); // TODO: Get accurate versions +ceil :: proc(x: f32) -> f32 { if x < 0 do f32(i64(x)) return f32(i64(x+1)); }// TODO: Get accurate versions +ceil :: proc(x: f64) -> f64 { if x < 0 do f64(i64(x)) return f64(i64(x+1)); }// TODO: Get accurate versions remainder :: proc(x, y: f32) -> f32 do return x - round(x/y) * y; remainder :: proc(x, y: f64) -> f64 do return x - round(x/y) * y; @@ -133,17 +133,20 @@ norm :: proc(v: $T/[vector 4]$E) -> T do return v / mag(v); norm0 :: proc(v: $T/[vector 2]$E) -> T { m := mag(v); - return m == 0 ? 0 : v/m; + if m == 0 do return 0; + return v/m; } norm0 :: proc(v: $T/[vector 3]$E) -> T { m := mag(v); - return m == 0 ? 0 : v/m; + if m == 0 do return 0; + return v/m; } norm0 :: proc(v: $T/[vector 4]$E) -> T { m := mag(v); - return m == 0 ? 0 : v/m; + if m == 0 do return 0; + return v/m; } diff --git a/core/strconv.odin b/core/strconv.odin index dbe7c20fb..a4a7a43f1 100644 --- a/core/strconv.odin +++ b/core/strconv.odin @@ -67,7 +67,8 @@ parse_i128 :: proc(s: string) -> i128 { value += v; } - return neg ? -value : value; + if neg do return -value; + return value; } parse_u128 :: proc(s: string) -> u128 { @@ -91,19 +92,15 @@ parse_u128 :: proc(s: string) -> u128 { value: u128; for r in s { - if r == '_' { - continue; - } - + if r == '_' do continue; v := u128(_digit_value(r)); - if v >= base { - break; - } + if v >= base do break; value *= base; value += u128(v); } - return neg ? -value : value; + if neg do return -value; + return value; } @@ -127,6 +124,7 @@ parse_f64 :: proc(s: string) -> f64 { for ; i < len(s); i += 1 { r := rune(s[i]); if r == '_' do continue; + v := _digit_value(r); if v >= 10 do break; value *= 10; @@ -139,13 +137,10 @@ parse_f64 :: proc(s: string) -> f64 { for ; i < len(s); i += 1 { r := rune(s[i]); - if r == '_' { - continue; - } + if r == '_' do continue; + v := _digit_value(r); - if v >= 10 { - break; - } + if v >= 10 do break; value += f64(v)/pow10; pow10 *= 10; } @@ -165,13 +160,10 @@ parse_f64 :: proc(s: string) -> f64 { exp: u32 = 0; for ; i < len(s); i += 1 { r := rune(s[i]); - if r == '_' { - continue; - } + if r == '_' do continue; + d := u32(_digit_value(r)); - if d >= 10 { - break; - } + if d >= 10 do break; exp = exp * 10 + d; } if exp > 308 { exp = 308; } @@ -181,13 +173,17 @@ parse_f64 :: proc(s: string) -> f64 { for exp > 0 { scale *= 10; exp -= 1; } } - return sign * (frac ? (value/scale) : (value*scale)); + if frac do return sign * (value/scale); + return sign * (value*scale); } append_bool :: proc(buf: []u8, b: bool) -> string { - s := b ? "true" : "false"; - append(&buf, ...cast([]u8)s); + if b { + append(&buf, "true"); + } else { + append(&buf, "false"); + } return string(buf); } diff --git a/src/array.cpp b/src/array.cpp index b382d3262..c1e47342c 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -10,12 +10,16 @@ struct Array { isize capacity; T &operator[](isize index) { - GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #if !defined(NO_ARRAY_BOUNDS_CHECK) + GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #endif return data[index]; } T const &operator[](isize index) const { - GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #if !defined(NO_ARRAY_BOUNDS_CHECK) + GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count); + #endif return data[index]; } }; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index dbe8aac61..6c550809c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -65,6 +65,12 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name)); e->type = t_invalid; return nullptr; + } else if (is_type_empty_union(t)) { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; } if (is_type_bit_field_value(t)) { t = default_bit_field_value_type(t); @@ -558,11 +564,18 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count if (type_expr != nullptr) { e->type = check_type(c, type_expr); } - if (e->type != nullptr && is_type_polymorphic(base_type(e->type))) { - gbString str = type_to_string(e->type); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name)); - e->type = t_invalid; + if (e->type != nullptr) { + if (is_type_polymorphic(base_type(e->type))) { + gbString str = type_to_string(e->type); + defer (gb_string_free(str)); + error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name)); + e->type = t_invalid; + } else if (is_type_empty_union(e->type)) { + gbString str = type_to_string(e->type); + defer (gb_string_free(str)); + error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name)); + e->type = t_invalid; + } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 23c823186..cd467cce1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -149,8 +149,7 @@ bool check_is_assignable_to_using_subtype(Type *src, Type *dst) { } bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Type *type, - Array *param_operands, PolyProcData *poly_proc_data, - bool check_later) { + Array *param_operands, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// // // // TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! // @@ -223,7 +222,6 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ - CheckerContext prev_context = c->context; defer (c->context = prev_context); @@ -238,6 +236,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ // c->context.no_polymorphic_errors = false; } + bool generate_type_again = c->context.no_polymorphic_errors; auto *pt = &src->Proc; @@ -251,6 +250,8 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ return false; } + + auto *found_gen_procs = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier)); if (found_gen_procs) { auto procs = *found_gen_procs; @@ -269,7 +270,6 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ if (generate_type_again) { // LEAK TODO(bill): This is technically a memory leak as it has to generate the type twice - bool prev_no_polymorphic_errors = c->context.no_polymorphic_errors; defer (c->context.no_polymorphic_errors = prev_no_polymorphic_errors); c->context.no_polymorphic_errors = false; @@ -283,6 +283,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ return false; } + if (found_gen_procs) { auto procs = *found_gen_procs; for_array(i, procs) { @@ -298,9 +299,10 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ } } + AstNode *proc_lit = clone_ast_node(a, old_decl->proc_lit); ast_node(pl, ProcLit, proc_lit); - // NOTE(bill): Associate the scope declared above with this procedure declaration's type + // NOTE(bill): Associate the scope declared above withinth this procedure declaration's type add_scope(c, pl->type, final_proc_type->Proc.scope); final_proc_type->Proc.is_poly_specialized = true; final_proc_type->Proc.is_polymorphic = true; @@ -350,16 +352,13 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ GB_ASSERT(entity != nullptr); - if (poly_proc_data) { poly_proc_data->gen_entity = entity; poly_proc_data->proc_info = proc_info; } - if (check_later) { - // NOTE(bill): Check the newly generated procedure body - check_procedure_later(c, proc_info); - } + // NOTE(bill): Check the newly generated procedure body + check_procedure_later(c, proc_info); return true; } @@ -368,11 +367,11 @@ bool check_polymorphic_procedure_assignment(Checker *c, Operand *operand, Type * if (operand->expr == NULL) return false; Entity *base_entity = entity_of_ident(&c->info, operand->expr); if (base_entity == nullptr) return false; - return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_proc_data, true); + return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_proc_data); } bool find_or_generate_polymorphic_procedure_from_parameters(Checker *c, Entity *base_entity, Array *operands, PolyProcData *poly_proc_data) { - return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_proc_data, false); + return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_proc_data); } bool check_type_specialization_to(Checker *c, Type *specialization, Type *type, bool compound, bool modify_type); @@ -829,6 +828,16 @@ void check_struct_field_decl(Checker *c, AstNode *decl, Array *fields, type = t_invalid; } + if (is_type_empty_union(type)) { + error(vd->names[0], "An empty union cannot be used as a field type in %.*s", LIT(context)); + type = t_invalid; + } + if (!c->context.allow_polymorphic_types && is_type_polymorphic(base_type(type))) { + error(vd->names[0], "Invalid use of a polymorphic type in %.*s", LIT(context)); + type = t_invalid; + } + + Array default_values = {}; defer (array_free(&default_values)); if (vd->values.count > 0 && allow_default_values) { @@ -1836,7 +1845,7 @@ Type *determine_type_from_polymorphic(Checker *c, Type *poly_type, Operand opera } -Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, Array *operands) { +Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, isize *specialization_count_, Array *operands) { if (_params == nullptr) { return nullptr; } @@ -2006,6 +2015,12 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari } type = t_invalid; } + if (is_type_empty_union(type)) { + gbString str = type_to_string(type); + error(params[i], "Invalid use of an empty union `%s`", str); + gb_string_free(str); + type = t_invalid; + } if (p->flags&FieldFlag_c_vararg) { @@ -2046,6 +2061,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari type = t_invalid; } bool modify_type = !c->context.no_polymorphic_errors; + if (specialization != nullptr && !check_type_specialization_to(c, specialization, type, false, modify_type)) { if (!c->context.no_polymorphic_errors) { gbString t = type_to_string(type); @@ -2105,10 +2121,25 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari } } + isize specialization_count = 0; + if (scope != nullptr) { + for_array(i, scope->elements.entries) { + Entity *e = scope->elements.entries[i].value; + if (e->kind == Type_Named) { + Type *t = e->type; + if (t->kind == Type_Generic && + t->Generic.specialized != nullptr) { + specialization_count += 1; + } + } + } + } + Type *tuple = make_type_tuple(c->allocator); tuple->Tuple.variables = variables; if (success_) *success_ = success; + if (specialization_count_) *specialization_count_ = specialization_count; if (is_variadic_) *is_variadic_ = is_variadic; return tuple; @@ -2425,7 +2456,8 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array bool variadic = false; bool success = true; - Type *params = check_get_params(c, c->context.scope, pt->params, &variadic, &success, operands); + isize specialization_count = 0; + Type *params = check_get_params(c, c->context.scope, pt->params, &variadic, &success, &specialization_count, operands); Type *results = check_get_results(c, c->context.scope, pt->results); isize param_count = 0; @@ -2433,15 +2465,16 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array if (params) param_count = params ->Tuple.variables.count; if (results) result_count = results->Tuple.variables.count; - type->Proc.node = proc_type_node; - type->Proc.scope = c->context.scope; - type->Proc.params = params; - type->Proc.param_count = param_count; - type->Proc.results = results; - type->Proc.result_count = result_count; - type->Proc.variadic = variadic; - type->Proc.calling_convention = pt->calling_convention; - type->Proc.is_polymorphic = pt->generic; + type->Proc.node = proc_type_node; + type->Proc.scope = c->context.scope; + type->Proc.params = params; + type->Proc.param_count = param_count; + type->Proc.results = results; + type->Proc.result_count = result_count; + type->Proc.variadic = variadic; + type->Proc.calling_convention = pt->calling_convention; + type->Proc.is_polymorphic = pt->generic; + type->Proc.specialization_count = specialization_count; if (param_count > 0) { Entity *end = params->Tuple.variables[param_count-1]; @@ -2886,6 +2919,10 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) e->TypeName.is_type_alias = true; add_entity(c, ps, ident, e); add_entity(c, s, ident, e); + } else { + error(ident, "Invalid use of a polymorphic type `$%.*s`", LIT(token.string)); + *type = t_invalid; + return false; } *type = t; return true; @@ -2945,25 +2982,19 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) error(at->count, "... can only be used in conjuction with compound literals"); count = 0; } -#if 0 - i64 esz = type_size_of(c->allocator, elem); - if (esz == 0) { + if (is_type_empty_union(elem)) { gbString str = type_to_string(elem); - error(at->elem, "Zero sized element type `%s` is not allowed", str); + error(at->elem, "An empty union `%s` is not allowed as an array element type", str); gb_string_free(str); } -#endif *type = make_type_array(c->allocator, elem, count); } else { Type *elem = check_type(c, at->elem); -#if 0 - i64 esz = type_size_of(c->allocator, elem); - if (esz == 0) { + if (is_type_empty_union(elem)) { gbString str = type_to_string(elem); - error(at->elem, "Zero sized element type `%s` is not allowed", str); + error(at->elem, "An empty union `%s` is not allowed as an slice element type", str); gb_string_free(str); } -#endif *type = make_type_slice(c->allocator, elem); } return true; @@ -2971,14 +3002,11 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) case_ast_node(dat, DynamicArrayType, e); Type *elem = check_type(c, dat->elem); - i64 esz = type_size_of(c->allocator, elem); -#if 0 - if (esz == 0) { + if (is_type_empty_union(elem)) { gbString str = type_to_string(elem); - error(dat->elem, "Zero sized element type `%s` is not allowed", str); + error(dat->elem, "An empty union `%s` is not allowed as an dynamic array element type", str); gb_string_free(str); } -#endif *type = make_type_dynamic_array(c->allocator, elem); return true; case_end; @@ -2989,7 +3017,11 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) Type *elem = check_type(c, vt->elem); Type *be = base_type(elem); i64 count = check_array_or_map_count(c, vt->count, false); - if (is_type_vector(be) || (!is_type_boolean(be) && !is_type_numeric(be) && be->kind != Type_Generic)) { + if (!c->context.allow_polymorphic_types && is_type_polymorphic(base_type(elem))) { + gbString str = type_to_string(elem); + error(vt->elem, "Invalid use of a polymorphic type `%s` as an dynamic array element type", str); + gb_string_free(str); + } else if (is_type_vector(be) || (!is_type_boolean(be) && !is_type_numeric(be) && !is_type_polymorphic(base_type(elem)))) { gbString err_str = type_to_string(elem); error(vt->elem, "Vector element type must be numerical or a boolean, got `%s`", err_str); gb_string_free(err_str); @@ -3018,16 +3050,6 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) return true; case_end; -/* case_ast_node(rut, RawUnionType, e); - *type = make_type_raw_union(c->allocator); - set_base_type(named_type, *type); - check_open_scope(c, e); - check_raw_union_type(c, *type, e); - check_close_scope(c); - (*type)->Struct.node = e; - return true; - case_end; - */ case_ast_node(et, EnumType, e); *type = make_type_enum(c->allocator); set_base_type(named_type, *type); @@ -4815,7 +4837,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id bool vari_expand = (ce->ellipsis.pos.line != 0); // if (vari_expand && id != BuiltinProc_append) { - // error(ce->ellipsis, "Invalid use of `..` with built-in procedure `append`"); + // error(ce->ellipsis, "Invalid use of `...` with built-in procedure `append`"); // return false; // } @@ -5914,6 +5936,7 @@ int valid_proc_and_score_cmp(void const *a, void const *b) { return sj < si ? -1 : sj > si; } + bool check_unpack_arguments(Checker *c, isize lhs_count, Array *operands, Array rhs, bool allow_ok) { bool optional_ok = false; for_array(i, rhs) { @@ -6009,14 +6032,14 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (vari_expand && !variadic) { if (show_error) { error(ce->ellipsis, - "Cannot use `..` in call to a non-variadic procedure: `%.*s`", + "Cannot use `...` in call to a non-variadic procedure: `%.*s`", LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; } else if (vari_expand && pt->c_vararg) { if (show_error) { error(ce->ellipsis, - "Cannot use `..` in call to a `#c_vararg` variadic procedure: `%.*s`", + "Cannot use `...` in call to a `#c_vararg` variadic procedure: `%.*s`", LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; @@ -6110,7 +6133,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { t = slice; if (operand_index != param_count) { if (show_error) { - error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end"); + error(o.expr, "`...` in a variadic procedure can only have one variadic argument at the end"); } if (data) { data->score = score; @@ -6130,15 +6153,6 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { score += s; } } - - if (gen_entity != nullptr && gen_entity->token.string == "append" && err != CallArgumentError_None) { - gb_printf_err("append %s with score %lld %d\n", type_to_string(final_proc_type), score, err); - } - - if (err == CallArgumentError_None && poly_proc_data.proc_info.decl != nullptr) { - // NOTE(bill): Check the newly generated procedure body - check_procedure_later(c, poly_proc_data.proc_info); - } } } @@ -6281,9 +6295,6 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { PolyProcData poly_proc_data = {}; if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, &poly_proc_data)) { gen_entity = poly_proc_data.gen_entity; - if (poly_proc_data.proc_info.decl != nullptr) { - check_procedure_later(c, poly_proc_data.proc_info); - } Type *gept = base_type(gen_entity->type); GB_ASSERT(is_type_proc(gept)); pt = &gept->Proc; @@ -6354,7 +6365,7 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t bool vari_expand = (ce->ellipsis.pos.line != 0); if (vari_expand) { - // error(ce->ellipsis, "Invalid use of `..` with `field = value` call`"); + // error(ce->ellipsis, "Invalid use of `...` with `field = value` call`"); } } else { @@ -6594,7 +6605,7 @@ CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, As bool vari_expand = (ce->ellipsis.pos.line != 0); if (vari_expand) { - error(ce->ellipsis, "Invalid use of `..` in a polymorphic type call`"); + error(ce->ellipsis, "Invalid use of `...` in a polymorphic type call`"); } } else { @@ -7735,9 +7746,13 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t if (!ok) { gbString expr_str = expr_to_string(o->expr); gbString dst_type_str = type_to_string(t); - error(o->expr, "Cannot type assert `%s` to `%s` as it is not a variant of that union", expr_str, dst_type_str); - gb_string_free(dst_type_str); - gb_string_free(expr_str); + defer (gb_string_free(expr_str)); + defer (gb_string_free(dst_type_str)); + if (bsrc->Union.variants.count == 0) { + error(o->expr, "Cannot type assert `%s` to `%s` as this is an empty union", expr_str, dst_type_str); + } else { + error(o->expr, "Cannot type assert `%s` to `%s` as it is not a variant of that union", expr_str, dst_type_str); + } o->mode = Addressing_Invalid; o->expr = node; return kind; @@ -8021,7 +8036,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t return kind; } else { Type *t = base_type(o->type); - if (t->kind == Type_Pointer) { + if (t->kind == Type_Pointer && !is_type_empty_union(t->Pointer.elem)) { if (o->mode != Addressing_Immutable) { o->mode = Addressing_Variable; } @@ -8315,7 +8330,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) { if (at->count != nullptr && at->count->kind == AstNode_UnaryExpr && at->count->UnaryExpr.op.kind == Token_Ellipsis) { - str = gb_string_appendc(str, ".."); + str = gb_string_appendc(str, "..."); } else { str = write_expr_to_string(str, at->count); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a55e74350..189bff7fd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1733,6 +1733,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { error(vd->type, "Invalid use of a polymorphic type `%s` in variable declaration", str); gb_string_free(str); init_type = t_invalid; + } else if (is_type_empty_union(init_type)) { + gbString str = type_to_string(init_type); + error(vd->type, "An empty union `%s` cannot be instantiated in variable declaration", str); + gb_string_free(str); + init_type = t_invalid; } } diff --git a/src/checker.cpp b/src/checker.cpp index 0f628a84a..4c77f25ee 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -185,7 +185,7 @@ struct DeclInfo { AstNode * type_expr; AstNode * init_expr; - AstNode * proc_lit; // AstNode_ProcLit + AstNode * proc_lit; // AstNode_ProcLit Type * gen_proc_type; // Precalculated Map deps; // Key: Entity * diff --git a/src/entity.cpp b/src/entity.cpp index d2c29ca98..21ef9095f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -98,6 +98,7 @@ struct Entity { } Variable; struct { bool is_type_alias; + Type *type_parameter_specialization; } TypeName; struct { OverloadKind overload_kind; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 78090e6ac..23c385140 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -282,21 +282,20 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { return; case Type_Union: { - // NOTE(bill): The zero size array is used to fix the alignment used in a structure as - // LLVM takes the first element's alignment as the entire alignment (like C) - i64 align = type_align_of(heap_allocator(), t); - i64 total_size = type_size_of(heap_allocator(), t); - #if 1 - i64 block_size = t->Union.variant_block_size; + if (t->Union.variants.count == 0) { + ir_fprintf(f, "%%..opaque"); + } else { + // NOTE(bill): The zero size array is used to fix the alignment used in a structure as + // LLVM takes the first element's alignment as the entire alignment (like C) + i64 align = type_align_of(heap_allocator(), t); + i64 total_size = type_size_of(heap_allocator(), t); + i64 block_size = t->Union.variant_block_size; - ir_fprintf(f, "{[0 x <%lld x i8>], ", align); - ir_fprintf(f, "[%lld x i8], ", block_size); - ir_print_type(f, m, t_type_info_ptr); - ir_fprintf(f, "}"); - #else - i64 block_size = total_size - build_context.word_size; - ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align, block_size, word_bits); - #endif + ir_fprintf(f, "{[0 x <%lld x i8>], ", align); + ir_fprintf(f, "[%lld x i8], ", block_size); + ir_print_type(f, m, t_type_info_ptr); + ir_fprintf(f, "}"); + } } return; case Type_Struct: { @@ -1733,6 +1732,8 @@ void print_llvm_ir(irGen *ir) { irFileBuffer buf = {}, *f = &buf; ir_file_buffer_init(f, &ir->output_file); + ir_print_encoded_local(f, str_lit("..opaque")); + ir_fprintf(f, " = type opaque;\n"); ir_print_encoded_local(f, str_lit("..string")); ir_fprintf(f, " = type {i8*, "); ir_print_type(f, m, t_int); diff --git a/src/parser.cpp b/src/parser.cpp index 21f3e3df4..9b848ca7e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -633,7 +633,8 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { case AstNode_BasicDirective: break; case AstNode_PolyType: - n->PolyType.type = clone_ast_node(a, n->PolyType.type); + n->PolyType.type = clone_ast_node(a, n->PolyType.type); + n->PolyType.specialization = clone_ast_node(a, n->PolyType.specialization); break; case AstNode_Ellipsis: n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr); @@ -833,6 +834,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { break; case AstNode_TypeType: + n->TypeType.specialization = clone_ast_node(a, n->TypeType.specialization); break; case AstNode_HelperType: n->HelperType.type = clone_ast_node(a, n->HelperType.type); diff --git a/src/types.cpp b/src/types.cpp index 360b9ed15..e7f183e79 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -146,6 +146,7 @@ struct TypeStruct { bool c_vararg; \ bool is_polymorphic; \ bool is_poly_specialized; \ + isize specialization_count; \ ProcCallingConvention calling_convention; \ }) \ TYPE_KIND(Map, struct { \ @@ -873,6 +874,11 @@ bool is_type_untyped_undef(Type *t) { } +bool is_type_empty_union(Type *t) { + t = base_type(t); + return t->kind == Type_Union && t->Union.variants.count == 0; +} + bool is_type_valid_for_keys(Type *t) { t = core_type(t); @@ -1822,6 +1828,9 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) { return type_align_of_internal(allocator, t->Enum.base_type, path); case Type_Union: { + if (t->Union.variants.count == 0) { + return 1; + } i64 max = build_context.word_size; for_array(i, t->Union.variants) { Type *variant = t->Union.variants[i]; @@ -2056,6 +2065,9 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) { return type_size_of_internal(allocator, t->Enum.base_type, path); case Type_Union: { + if (t->Union.variants.count == 0) { + return 0; + } i64 align = type_align_of_internal(allocator, t, path); if (path->failure) { return FAILURE_SIZE; @@ -2294,7 +2306,7 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_Union: - str = gb_string_appendc(str, "union{"); + str = gb_string_appendc(str, "union {"); for_array(i, type->Union.variants) { Type *t = type->Union.variants[i]; if (i > 0) str = gb_string_appendc(str, ", "); @@ -2304,40 +2316,22 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_Struct: { - if (type->Struct.is_raw_union) { - str = gb_string_appendc(str, "raw_union{"); - for_array(i, type->Struct.fields) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable); - if (i > 0) { - str = gb_string_appendc(str, ", "); - } - str = gb_string_append_length(str, f->token.string.text, f->token.string.len); - str = gb_string_appendc(str, ": "); - str = write_type_to_string(str, f->type); - } - str = gb_string_appendc(str, "}"); - } else { str = gb_string_appendc(str, "struct"); - if (type->Struct.is_packed) { - str = gb_string_appendc(str, " #packed"); + if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed"); + if (type->Struct.is_ordered) str = gb_string_appendc(str, " #ordered"); + if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); + str = gb_string_appendc(str, " {"); + for_array(i, type->Struct.fields) { + Entity *f = type->Struct.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) { + str = gb_string_appendc(str, ", "); } - if (type->Struct.is_ordered) { - str = gb_string_appendc(str, " #ordered"); - } - str = gb_string_appendc(str, " {"); - for_array(i, type->Struct.fields) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable); - if (i > 0) { - str = gb_string_appendc(str, ", "); - } - str = gb_string_append_length(str, f->token.string.text, f->token.string.len); - str = gb_string_appendc(str, ": "); - str = write_type_to_string(str, f->type); - } - str = gb_string_appendc(str, "}"); + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); } + str = gb_string_appendc(str, "}"); } break; case Type_Map: { @@ -2373,7 +2367,7 @@ gbString write_type_to_string(gbString str, Type *type) { } if (var->flags&EntityFlag_Ellipsis) { Type *slice = base_type(var->type); - str = gb_string_appendc(str, ".."); + str = gb_string_appendc(str, "..."); GB_ASSERT(var->type->kind == Type_Slice); str = write_type_to_string(str, slice->Slice.elem); } else {