From 42ab882db4a6d5765c68021ade010b468ff4531e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 Jan 2022 15:56:26 +0000 Subject: Remove debug code --- src/check_expr.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 81f69055a..1742ef2d8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3419,7 +3419,6 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ if (operand->value.kind == ExactValue_String) { String key = operand->value.value_string; if (is_type_string(operand->type) && is_type_enum(target_type)) { - gb_printf_err("HERE!\n"); Type *et = base_type(target_type); check_did_you_mean_type(key, et->Enum.fields, "."); } -- cgit v1.2.3 From fe0b5bf4e27912c49f6c5eab817cbf514b0b22e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 Jan 2022 23:28:59 +0000 Subject: Parse comments on enums fields --- src/check_expr.cpp | 7 +++++++ src/check_type.cpp | 23 ++++++++++++----------- src/docs_writer.cpp | 11 ++++++----- src/entity.cpp | 2 ++ src/parser.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- src/parser.hpp | 6 ++++++ 6 files changed, 74 insertions(+), 17 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1742ef2d8..725b57f33 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9341,6 +9341,13 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = gb_string_appendc(str, " = "); str = write_expr_to_string(str, fv->value, shorthand); case_end; + case_ast_node(fv, EnumFieldValue, node); + str = write_expr_to_string(str, fv->name, shorthand); + if (fv->value) { + str = gb_string_appendc(str, " = "); + str = write_expr_to_string(str, fv->value, shorthand); + } + case_end; case_ast_node(ht, HelperType, node); str = gb_string_appendc(str, "#type "); diff --git a/src/check_type.cpp b/src/check_type.cpp index 2a7479d68..a6d82c86e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -732,20 +732,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast Ast *ident = nullptr; Ast *init = nullptr; u32 entity_flags = 0; - if (field->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, field); - if (fv->field == nullptr || fv->field->kind != Ast_Ident) { - error(field, "An enum field's name must be an identifier"); - continue; - } - ident = fv->field; - init = fv->value; - } else if (field->kind == Ast_Ident) { - ident = field; - } else { + if (field->kind != Ast_EnumFieldValue) { error(field, "An enum field's name must be an identifier"); continue; } + ident = field->EnumFieldValue.name; + init = field->EnumFieldValue.value; + if (ident == nullptr || ident->kind != Ast_Ident) { + error(field, "An enum field's name must be an identifier"); + continue; + } + CommentGroup *docs = field->EnumFieldValue.docs; + CommentGroup *comment = field->EnumFieldValue.comment; + String name = ident->Ident.token.string; if (init != nullptr) { @@ -803,6 +802,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast e->flags |= EntityFlag_Visited; e->state = EntityState_Resolved; e->Constant.flags |= entity_flags; + e->Constant.docs = docs; + e->Constant.comment = comment; if (scope_lookup_current(ctx->scope, name) != nullptr) { error(ident, "'%.*s' is already declared in this enumeration", LIT(name)); diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 825ca113f..b1b9450df 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -811,11 +811,12 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { comment = e->decl_info->comment; docs = e->decl_info->docs; } - if (!comment && e->kind == Entity_Variable) { - comment = e->Variable.comment; - } - if (!docs && e->kind == Entity_Variable) { - docs = e->Variable.docs; + if (e->kind == Entity_Variable) { + if (!comment) { comment = e->Variable.comment; } + if (!docs) { docs = e->Variable.docs; } + } else if (e->kind == Entity_Constant) { + if (!comment) { comment = e->Constant.comment; } + if (!docs) { docs = e->Constant.docs; } } String link_name = {}; diff --git a/src/entity.cpp b/src/entity.cpp index 0f8bfa456..a0438a9f4 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -161,6 +161,8 @@ struct Entity { ParameterValue param_value; u32 flags; i32 field_group_index; + CommentGroup *docs; + CommentGroup *comment; } Constant; struct { Ast *init_expr; // only used for some variables within procedure bodies diff --git a/src/parser.cpp b/src/parser.cpp index 076c698ff..ebe65cee1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -693,6 +693,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) { return result; } + +Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) { + Ast *result = alloc_ast_node(f, Ast_EnumFieldValue); + result->EnumFieldValue.name = name; + result->EnumFieldValue.value = value; + result->EnumFieldValue.docs = docs; + result->EnumFieldValue.comment = comment; + return result; +} + Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_CompoundLit); result->CompoundLit.type = type; @@ -1689,6 +1699,36 @@ Array parse_element_list(AstFile *f) { return elems; } +Array parse_enum_field_list(AstFile *f) { + auto elems = array_make(heap_allocator()); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + CommentGroup *docs = f->lead_comment; + CommentGroup *comment = nullptr; + Ast *name = parse_value(f); + Ast *value = nullptr; + if (f->curr_token.kind == Token_Eq) { + Token eq = expect_token(f, Token_Eq); + value = parse_value(f); + } + + comment = f->line_comment; + + Ast *elem = ast_enum_field_value(f, name, value, docs, comment); + array_add(&elems, elem); + + if (!allow_token(f, Token_Comma)) { + break; + } + + if (!elem->EnumFieldValue.comment) { + elem->EnumFieldValue.comment = f->line_comment; + } + } + + return elems; +} Ast *parse_literal_value(AstFile *f, Ast *type) { Array elems = {}; @@ -2449,7 +2489,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { skip_possible_newline_for_literal(f); Token open = expect_token(f, Token_OpenBrace); - Array values = parse_element_list(f); + Array values = parse_enum_field_list(f); Token close = expect_closing_brace_of_field_list(f); return ast_enum_type(f, token, base_type, values); diff --git a/src/parser.hpp b/src/parser.hpp index b83822cbf..b005a4465 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -383,6 +383,12 @@ AST_KIND(_ExprBegin, "", bool) \ void *sce_temp_data; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ + AST_KIND(EnumFieldValue, "enum field value", struct { \ + Ast *name; \ + Ast *value; \ + CommentGroup *docs; \ + CommentGroup *comment; \ + }) \ AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \ -- cgit v1.2.3 From f16f1d932ef36c67dab0b7c8cd6898f9811d6f75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 25 Jan 2022 14:24:15 +0000 Subject: Fix #1448 --- src/check_expr.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 725b57f33..276e9d0bb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5755,8 +5755,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.curr_proc_sig = e->type; GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } } return data; } @@ -5769,6 +5773,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type Entity *e = entity_of_node(ident); + CallArgumentData data = {}; CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); @@ -5777,7 +5782,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (entity_to_use != nullptr) { update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); } - if (data.gen_entity != nullptr) { Entity *e = data.gen_entity; DeclInfo *decl = data.gen_entity->decl_info; @@ -5789,8 +5793,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.curr_proc_sig = e->type; GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } } return data; } -- cgit v1.2.3 From 24e7356825a473cba0a1e9962470be73d60ad248 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 27 Jan 2022 16:08:47 +0000 Subject: Add `#no_type_assert` and `#type_assert` to disable implicit type assertions with `x.(T)` --- src/check_expr.cpp | 8 +++++ src/check_stmt.cpp | 8 +++++ src/checker.cpp | 12 ++++++++ src/llvm_backend_expr.cpp | 72 +++++++++++++++++++++++++------------------- src/llvm_backend_stmt.cpp | 7 +++++ src/llvm_backend_utility.cpp | 41 ++++++++++++++++--------- src/parser.cpp | 36 ++++++++++++++++++++++ src/parser.hpp | 4 +++ 8 files changed, 142 insertions(+), 46 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 276e9d0bb..fb5a90f5a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6883,6 +6883,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type out &= ~StateFlag_no_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } + c->state_flags = out; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 94b7561c7..f9e55ab37 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) { out &= ~StateFlag_no_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } + ctx->state_flags = out; } diff --git a/src/checker.cpp b/src/checker.cpp index e0c756bb8..038709056 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4875,6 +4875,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0; + bool type_assert = (pi->tags & ProcTag_type_assert) != 0; + bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0; + if (bounds_check) { ctx.state_flags |= StateFlag_bounds_check; ctx.state_flags &= ~StateFlag_no_bounds_check; @@ -4882,6 +4885,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc ctx.state_flags |= StateFlag_no_bounds_check; ctx.state_flags &= ~StateFlag_bounds_check; } + + if (type_assert) { + ctx.state_flags |= StateFlag_type_assert; + ctx.state_flags &= ~StateFlag_no_type_assert; + } else if (no_type_assert) { + ctx.state_flags |= StateFlag_no_type_assert; + ctx.state_flags &= ~StateFlag_type_assert; + } + if (pi->body != nullptr && e != nullptr) { GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0); } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 9b2e26434..ea031ee56 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2768,27 +2768,29 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { Type *src_type = type_deref(v.type); Type *dst_type = type; - lbValue src_tag = {}; - lbValue dst_tag = {}; - if (is_type_union_maybe_pointer(src_type)) { - src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v); - dst_tag = lb_const_bool(p->module, t_bool, true); - } else { - src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); - dst_tag = lb_const_union_tag(p->module, src_type, dst_type); - } - lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); - auto args = array_make(permanent_allocator(), 6); - args[0] = ok; + if ((p->state_flags & StateFlag_no_type_assert) == 0) { + lbValue src_tag = {}; + lbValue dst_tag = {}; + if (is_type_union_maybe_pointer(src_type)) { + src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v); + dst_tag = lb_const_bool(p->module, t_bool, true); + } else { + src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); + dst_tag = lb_const_union_tag(p->module, src_type, dst_type); + } + lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); + auto args = array_make(permanent_allocator(), 6); + args[0] = ok; - args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(p->module, t_i32, pos.line); - args[3] = lb_const_int(p->module, t_i32, pos.column); + args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(p->module, t_i32, pos.line); + args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = lb_typeid(p->module, src_type); - args[5] = lb_typeid(p->module, dst_type); - lb_emit_runtime_call(p, "type_assertion_check", args); + args[4] = lb_typeid(p->module, src_type); + args[5] = lb_typeid(p->module, dst_type); + lb_emit_runtime_call(p, "type_assertion_check", args); + } lbValue data_ptr = v; return lb_emit_conv(p, data_ptr, tv.type); @@ -2797,23 +2799,23 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { if (is_type_pointer(v.type)) { v = lb_emit_load(p, v); } - lbValue data_ptr = lb_emit_struct_ev(p, v, 0); - lbValue any_id = lb_emit_struct_ev(p, v, 1); - lbValue id = lb_typeid(p->module, type); + if ((p->state_flags & StateFlag_no_type_assert) == 0) { + lbValue any_id = lb_emit_struct_ev(p, v, 1); + lbValue id = lb_typeid(p->module, type); + lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); + auto args = array_make(permanent_allocator(), 6); + args[0] = ok; - lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); - auto args = array_make(permanent_allocator(), 6); - args[0] = ok; - - args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(p->module, t_i32, pos.line); - args[3] = lb_const_int(p->module, t_i32, pos.column); + args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(p->module, t_i32, pos.line); + args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = any_id; - args[5] = id; - lb_emit_runtime_call(p, "type_assertion_check", args); + args[4] = any_id; + args[5] = id; + lb_emit_runtime_call(p, "type_assertion_check", args); + } return lb_emit_conv(p, data_ptr, tv.type); } else { @@ -2843,6 +2845,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { out &= ~StateFlag_bounds_check; } + if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } else if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } + p->state_flags = out; } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 3375ceda9..916c0433e 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1991,6 +1991,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { out |= StateFlag_no_bounds_check; out &= ~StateFlag_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } p->state_flags = out; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 5b1b11b44..7e2bd7daa 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -626,6 +626,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lbValue value_ = lb_address_from_load_or_generate_local(p, value); + if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) { + // just do a bit cast of the data at the front + lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type)); + return lb_emit_load(p, ptr); + } + lbValue tag = {}; lbValue dst_tag = {}; lbValue cond = {}; @@ -666,23 +672,22 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lb_start_block(p, end_block); if (!is_tuple) { - { - // NOTE(bill): Panic on invalid conversion - Type *dst_type = tuple->Tuple.variables[0]->type; + GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0); + // NOTE(bill): Panic on invalid conversion + Type *dst_type = tuple->Tuple.variables[0]->type; - lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); - args[0] = ok; + lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); + auto args = array_make(permanent_allocator(), 7); + args[0] = ok; - args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(m, t_i32, pos.line); - args[3] = lb_const_int(m, t_i32, pos.column); + args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(m, t_i32, pos.line); + args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = lb_typeid(m, src_type); - args[5] = lb_typeid(m, dst_type); - args[6] = lb_emit_conv(p, value_, t_rawptr); - lb_emit_runtime_call(p, "type_assertion_check2", args); - } + args[4] = lb_typeid(m, src_type); + args[5] = lb_typeid(m, dst_type); + args[6] = lb_emit_conv(p, value_, t_rawptr); + lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); } @@ -706,6 +711,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos } Type *dst_type = tuple->Tuple.variables[0]->type; + if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) { + // just do a bit cast of the data at the front + lbValue ptr = lb_emit_struct_ev(p, value, 0); + ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type)); + return lb_addr(ptr); + } + lbAddr v = lb_add_local_generated(p, tuple, true); lbValue dst_typeid = lb_typeid(m, dst_type); @@ -731,7 +743,6 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos if (!is_tuple) { // NOTE(bill): Panic on invalid conversion - lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); auto args = array_make(permanent_allocator(), 7); args[0] = ok; diff --git a/src/parser.cpp b/src/parser.cpp index 108411cd0..9cc9adfc9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1843,6 +1843,8 @@ void parse_proc_tags(AstFile *f, u64 *tags) { ELSE_IF_ADD_TAG(require_results) ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(no_bounds_check) + ELSE_IF_ADD_TAG(type_assert) + ELSE_IF_ADD_TAG(no_type_assert) else { syntax_error(tag_expr, "Unknown procedure type tag #%.*s", LIT(tag_name)); } @@ -1853,6 +1855,10 @@ void parse_proc_tags(AstFile *f, u64 *tags) { if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) { syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure"); } + + if ((*tags & ProcTag_type_assert) && (*tags & ProcTag_no_type_assert)) { + syntax_error(f->curr_token, "You cannot apply both #type_assert and #no_type_assert to a procedure"); + } } @@ -2000,11 +2006,23 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta syntax_error(tag_token, "#bounds_check and #no_bounds_check cannot be applied together"); } break; + case StateFlag_type_assert: + if ((s->state_flags & StateFlag_no_type_assert) != 0) { + syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together"); + } + break; + case StateFlag_no_type_assert: + if ((s->state_flags & StateFlag_type_assert) != 0) { + syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together"); + } + break; } switch (state_flag) { case StateFlag_bounds_check: case StateFlag_no_bounds_check: + case StateFlag_type_assert: + case StateFlag_no_type_assert: switch (s->kind) { case Ast_BlockStmt: case Ast_IfStmt: @@ -2128,6 +2146,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { } else if (name.string == "no_bounds_check") { Ast *operand = parse_expr(f, lhs); return parse_check_directive_for_statement(operand, name, StateFlag_no_bounds_check); + } else if (name.string == "type_assert") { + Ast *operand = parse_expr(f, lhs); + return parse_check_directive_for_statement(operand, name, StateFlag_type_assert); + } else if (name.string == "no_type_assert") { + Ast *operand = parse_expr(f, lhs); + return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert); } else if (name.string == "relative") { Ast *tag = ast_basic_directive(f, token, name); tag = parse_call_expr(f, tag); @@ -2224,6 +2248,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (tags & ProcTag_bounds_check) { body->state_flags |= StateFlag_bounds_check; } + if (tags & ProcTag_no_type_assert) { + body->state_flags |= StateFlag_no_type_assert; + } + if (tags & ProcTag_type_assert) { + body->state_flags |= StateFlag_type_assert; + } return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { @@ -4611,6 +4641,12 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "no_bounds_check") { s = parse_stmt(f); return parse_check_directive_for_statement(s, name, StateFlag_no_bounds_check); + } else if (tag == "type_assert") { + s = parse_stmt(f); + return parse_check_directive_for_statement(s, name, StateFlag_type_assert); + } else if (tag == "no_type_assert") { + s = parse_stmt(f); + return parse_check_directive_for_statement(s, name, StateFlag_no_type_assert); } else if (tag == "partial") { s = parse_stmt(f); switch (s->kind) { diff --git a/src/parser.hpp b/src/parser.hpp index b005a4465..656f709e8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -226,6 +226,8 @@ enum ProcInlining { enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, + ProcTag_type_assert = 1<<2, + ProcTag_no_type_assert = 1<<3, ProcTag_require_results = 1<<4, ProcTag_optional_ok = 1<<5, @@ -258,6 +260,8 @@ ProcCallingConvention default_calling_convention(void) { enum StateFlag : u8 { StateFlag_bounds_check = 1<<0, StateFlag_no_bounds_check = 1<<1, + StateFlag_type_assert = 1<<2, + StateFlag_no_type_assert = 1<<3, StateFlag_BeenHandled = 1<<7, }; -- cgit v1.2.3 From 35c90fe12413335962cad02d77a4894079b00a5d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Feb 2022 13:34:31 +0000 Subject: Fix type alias declaration evaluation problem (#854 #1439) --- src/check_decl.cpp | 39 +++++++++++++++++- src/check_expr.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++++++------- src/checker.cpp | 3 -- 3 files changed, 137 insertions(+), 18 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f9bc17ba4..193c28aea 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -385,7 +385,44 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Operand operand = {}; if (init != nullptr) { - Entity *entity = nullptr; + Entity *entity = check_entity_from_ident_or_selector(ctx, init); + if (entity != nullptr && entity->kind == Entity_TypeName) { + // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases + // being "confused" as constants + // + // A :: B + // C :: proc "c" (^A) + // B :: struct {x: C} + // + // A gets evaluated first, and then checks B. + // B then checks C. + // C then tries to check A which is unresolved but thought to be a constant. + // Therefore within C's check, A errs as "not a type". + // + // This is because a const declaration may or may not be a type and this cannot + // be determined from a syntactical standpoint. + // This check allows the compiler to override the entity to be checked as a type. + // + // There is no problem if B is prefixed with the `#type` helper enforcing at + // both a syntax and semantic level that B must be a type. + // + // A :: #type B + // + // This approach is not fool proof and can fail in case such as: + // + // X :: type_of(x) + // X :: Foo(int).Type + // + // Since even these kind of declarations may cause weird checking cycles. + // For the time being, these are going to be treated as an unfortunate error + // until there is a proper delaying system to try declaration again if they + // have failed. + + e->kind = Entity_TypeName; + check_type_decl(ctx, e, init, named_type); + return; + } + entity = nullptr; if (init->kind == Ast_Ident) { entity = check_ident(ctx, &operand, init, nullptr, e->type, true); } else if (init->kind == Ast_SelectorExpr) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fb5a90f5a..88296611b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1286,7 +1286,6 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) { return false; } - Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) { GB_ASSERT(n->kind == Ast_Ident); o->mode = Addressing_Invalid; @@ -1422,8 +1421,12 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ case Entity_TypeName: o->mode = Addressing_Type; if (check_cycle(c, e, true)) { - type = t_invalid; + o->type = t_invalid; + } + if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) { + o->type = base_type(o->type); } + break; case Entity_ImportName: @@ -4064,6 +4067,98 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n } +bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) { + bool is_declared = entity != nullptr; + if (is_declared) { + if (entity->kind == Entity_Builtin) { + // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy + // This means that we should just ignore the found result through it + *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope; + } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) { + is_declared = false; + } + } + return is_declared; +} + +// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning +Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node) { + if (node->kind == Ast_Ident) { + String name = node->Ident.token.string; + return scope_lookup(c->scope, name); + } else if (node->kind == Ast_SelectorExpr) { + ast_node(se, SelectorExpr, node); + if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) { + return nullptr; + } + + Ast *op_expr = se->expr; + Ast *selector = unparen_expr(se->selector); + if (selector == nullptr) { + return nullptr; + } + if (selector->kind != Ast_Ident) { + return nullptr; + } + + Entity *entity = nullptr; + Entity *expr_entity = nullptr; + bool check_op_expr = true; + + if (op_expr->kind == Ast_Ident) { + String op_name = op_expr->Ident.token.string; + Entity *e = scope_lookup(c->scope, op_name); + add_entity_use(c, op_expr, e); + expr_entity = e; + + if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) { + // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile + // It pretty much needs to be in this order and this way + // If you can clean this up, please do but be really careful + String import_name = op_name; + Scope *import_scope = e->ImportName.scope; + String entity_name = selector->Ident.token.string; + + check_op_expr = false; + entity = scope_lookup_current(import_scope, entity_name); + bool allow_builtin = false; + if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { + return nullptr; + } + + check_entity_decl(c, entity, nullptr, nullptr); + if (entity->kind == Entity_ProcGroup) { + return entity; + } + GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind])); + } + } + + Operand operand = {}; + if (check_op_expr) { + check_expr_base(c, &operand, op_expr, nullptr); + if (operand.mode == Addressing_Invalid) { + return nullptr; + } + } + + if (entity == nullptr && selector->kind == Ast_Ident) { + String field_name = selector->Ident.token.string; + if (is_type_dynamic_array(type_deref(operand.type))) { + init_mem_allocator(c->checker); + } + auto sel = lookup_field(operand.type, field_name, operand.mode == Addressing_Type); + entity = sel.entity; + } + + if (entity != nullptr) { + return entity; + } + } + return nullptr; +} + + Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) { ast_node(se, SelectorExpr, node); @@ -4112,18 +4207,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ check_op_expr = false; entity = scope_lookup_current(import_scope, entity_name); - bool is_declared = entity != nullptr; bool allow_builtin = false; - if (is_declared) { - if (entity->kind == Entity_Builtin) { - // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy - // This means that we should just ignore the found result through it - allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope; - } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) { - is_declared = false; - } - } - if (!is_declared) { + if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); operand->mode = Addressing_Invalid; operand->expr = node; @@ -4213,7 +4298,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } } - if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) { + if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) { // TODO(bill): Simd_Vector swizzling String field_name = selector->Ident.token.string; diff --git a/src/checker.cpp b/src/checker.cpp index 4dcb5120f..c4423b2bc 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3563,9 +3563,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (is_ast_type(init)) { e = alloc_entity_type_name(d->scope, token, nullptr); - // if (vd->type != nullptr) { - // error(name, "A type declaration cannot have an type parameter"); - // } } else if (init->kind == Ast_ProcLit) { if (c->scope->flags&ScopeFlag_Type) { error(name, "Procedure declarations are not allowed within a struct"); -- cgit v1.2.3 From 3a81f2ab898d537bba51b9ea81e047652112574e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Feb 2022 22:40:15 +0000 Subject: Correct the type aliasing problem, caused by aliases (of aliases)+ --- src/check_decl.cpp | 1 + src/check_expr.cpp | 5 ++++- src/checker.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 193c28aea..2454feb33 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -387,6 +387,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, if (init != nullptr) { Entity *entity = check_entity_from_ident_or_selector(ctx, init); if (entity != nullptr && entity->kind == Entity_TypeName) { + // @TypeAliasingProblem // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases // being "confused" as constants // diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 88296611b..e1c9bde84 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4088,7 +4088,7 @@ Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node) { return scope_lookup(c->scope, name); } else if (node->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, node); - if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) { + if (se->token.kind == Token_ArrowRight) { return nullptr; } @@ -4108,6 +4108,9 @@ Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node) { if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; Entity *e = scope_lookup(c->scope, op_name); + if (e == nullptr) { + return nullptr; + } add_entity_use(c, op_expr, e); expr_entity = e; diff --git a/src/checker.cpp b/src/checker.cpp index c4423b2bc..c90f357dd 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3665,6 +3665,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) { check_collect_entities(&c, block->stmts); } +bool correct_single_type_alias(CheckerContext *c, Entity *e) { + if (e->kind == Entity_Constant) { + DeclInfo *d = e->decl_info; + if (d != nullptr && d->init_expr != nullptr) { + Ast *init = d->init_expr; + Entity *alias_of = check_entity_from_ident_or_selector(c, init); + if (alias_of != nullptr && alias_of->kind == Entity_TypeName) { + e->kind = Entity_TypeName; + return true; + } + } + } + return false; +} + +bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) { + isize n = s->elements.entries.count; + bool correction = false; + for (isize i = n-1; i >= 0; i--) { + correction |= correct_single_type_alias(c, s->elements.entries[i].value); + } + return correction; +} +bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) { + isize n = s->elements.entries.count; + bool correction = false; + for (isize i = 0; i < n; i++) { + correction |= correct_single_type_alias(c, s->elements.entries[i].value); + } + return correction; +} + + +void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) { + // NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases + // of type aliases being "confused" as constants + // + // A :: C + // B :: A + // C :: struct {b: ^B} + // + // See @TypeAliasingProblem for more information + for (;;) { + bool corrections = false; + corrections |= correct_type_alias_in_scope_backwards(c, s); + corrections |= correct_type_alias_in_scope_forwards(c, s); + if (!corrections) { + return; + } + } +} + + // NOTE(bill): If file_scopes == nullptr, this will act like a local scope void check_collect_entities(CheckerContext *c, Slice const &nodes) { AstFile *curr_file = nullptr; @@ -3736,6 +3789,7 @@ void check_collect_entities(CheckerContext *c, Slice const &nodes) { } } + // correct_type_aliases(c); // NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something // declared after this stmt in source @@ -4381,10 +4435,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice const &decls) { for_array(i, decls) { if (collect_file_decl(ctx, decls[i])) { + correct_type_aliases_in_scope(ctx, ctx->scope); return true; } } - + correct_type_aliases_in_scope(ctx, ctx->scope); return false; } @@ -4654,6 +4709,15 @@ void check_import_entities(Checker *c) { } add_untyped_expressions(ctx.info, &untyped); } + + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + reset_checker_context(&ctx, f, &untyped); + ctx.collect_delayed_decls = false; + + correct_type_aliases_in_scope(&ctx, pkg->scope); + } + for_array(i, pkg->files) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f, &untyped); -- cgit v1.2.3 From d5384c5aa4d823fbd527fb82e9eb5559b4266dfd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Feb 2022 22:45:13 +0000 Subject: Only check idents in the alias (of alias)+ problem --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 4 ++-- src/checker.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 2454feb33..63fc777c0 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -385,7 +385,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Operand operand = {}; if (init != nullptr) { - Entity *entity = check_entity_from_ident_or_selector(ctx, init); + Entity *entity = check_entity_from_ident_or_selector(ctx, init, false); if (entity != nullptr && entity->kind == Entity_TypeName) { // @TypeAliasingProblem // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e1c9bde84..d51444b4d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4082,11 +4082,11 @@ bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool * } // NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning -Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node) { +Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) { if (node->kind == Ast_Ident) { String name = node->Ident.token.string; return scope_lookup(c->scope, name); - } else if (node->kind == Ast_SelectorExpr) { + } else if (!ident_only) if (node->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, node); if (se->token.kind == Token_ArrowRight) { return nullptr; diff --git a/src/checker.cpp b/src/checker.cpp index c90f357dd..188d68502 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3670,7 +3670,7 @@ bool correct_single_type_alias(CheckerContext *c, Entity *e) { DeclInfo *d = e->decl_info; if (d != nullptr && d->init_expr != nullptr) { Ast *init = d->init_expr; - Entity *alias_of = check_entity_from_ident_or_selector(c, init); + Entity *alias_of = check_entity_from_ident_or_selector(c, init, true); if (alias_of != nullptr && alias_of->kind == Entity_TypeName) { e->kind = Entity_TypeName; return true; -- cgit v1.2.3 From b8c4bf2afb39ca2980b2827aa1775b35728bb195 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 14:02:21 +0000 Subject: Add `#partial [Enum]Type{...}` support to check for missing enumerated array fields --- core/compress/gzip/gzip.odin | 1 + core/math/big/common.odin | 1 + src/check_expr.cpp | 145 +++++++++++++++++++++++++++++++++++++++++++ src/check_stmt.cpp | 90 +-------------------------- 4 files changed, 149 insertions(+), 88 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 0ed805ef8..96e9c49a0 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -67,6 +67,7 @@ OS :: enum u8 { Unknown = 255, } OS_Name :: #sparse[OS]string{ + ._Unknown = "", .FAT = "FAT", .Amiga = "Amiga", .VMS = "VMS/OpenVMS", diff --git a/core/math/big/common.odin b/core/math/big/common.odin index e1198c352..74a641d83 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -182,6 +182,7 @@ Error_String :: #sparse[Error]string{ .Max_Iterations_Reached = "Max iterations reached", .Buffer_Overflow = "Buffer overflow", .Integer_Overflow = "Integer overflow", + .Integer_Underflow = "Integer underflow", .Division_by_Zero = "Division by zero", .Math_Domain_Error = "Math domain error", diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d51444b4d..40777df2a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6956,6 +6956,100 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ } +struct TypeAndToken { + Type *type; + Token token; +}; + +void add_constant_switch_case(CheckerContext *ctx, PtrMap *seen, Operand operand, bool use_expr = true) { + if (operand.mode != Addressing_Constant) { + return; + } + if (operand.value.kind == ExactValue_Invalid) { + return; + } + + uintptr key = hash_exact_value(operand.value); + TypeAndToken *found = map_get(seen, key); + if (found != nullptr) { + isize count = multi_map_count(seen, key); + TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); + + multi_map_get_all(seen, key, taps); + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (!are_types_identical(operand.type, tap.type)) { + continue; + } + + TokenPos pos = tap.token.pos; + if (use_expr) { + gbString expr_str = expr_to_string(operand.expr); + error(operand.expr, + "Duplicate case '%s'\n" + "\tprevious case at %s", + expr_str, + token_pos_to_string(pos)); + gb_string_free(expr_str); + } else { + error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos)); + } + return; + } + } + + TypeAndToken tap = {operand.type, ast_token(operand.expr)}; + multi_map_insert(seen, key, tap); +} + +typedef PtrMap SeenMap; + +void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) { + if (is_type_enum(x.type)) { + // TODO(bill): Fix this logic so it's fast!!! + + i64 v0 = exact_value_to_i64(lhs.value); + i64 v1 = exact_value_to_i64(rhs.value); + Operand v = {}; + v.mode = Addressing_Constant; + v.type = x.type; + v.expr = x.expr; + + Type *bt = base_type(x.type); + GB_ASSERT(bt->kind == Type_Enum); + for (i64 vi = v0; vi <= v1; vi++) { + if (upper_op != Token_GtEq && vi == v1) { + break; + } + + bool found = false; + for_array(j, bt->Enum.fields) { + Entity *f = bt->Enum.fields[j]; + GB_ASSERT(f->kind == Entity_Constant); + + i64 fv = exact_value_to_i64(f->Constant.value); + if (fv == vi) { + found = true; + break; + } + } + if (found) { + v.value = exact_value_i64(vi); + add_constant_switch_case(ctx, seen, v); + } + } + } else { + add_constant_switch_case(ctx, seen, lhs); + if (upper_op == Token_GtEq) { + add_constant_switch_case(ctx, seen, rhs); + } + } +} +void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) { + add_constant_switch_case(ctx, seen, x); +} + + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { u32 prev_state_flags = c->state_flags; defer (c->state_flags = prev_state_flags); @@ -7863,6 +7957,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (bet == t_invalid) { break; } + bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial"); + + SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue + map_init(&seen, heap_allocator()); + defer (map_destroy(&seen)); if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { RangeCache rc = range_cache_make(heap_allocator()); @@ -7936,6 +8035,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type check_assignment(c, &operand, elem_type, context_name); is_constant = is_constant && operand.mode == Addressing_Constant; + + add_to_seen_map(c, &seen, op.kind, x, x, y); } else { Operand op_index = {}; check_expr_with_type_hint(c, &op_index, fv->field, index_type); @@ -7971,6 +8072,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type check_assignment(c, &operand, elem_type, context_name); is_constant = is_constant && operand.mode == Addressing_Constant; + + add_to_seen_map(c, &seen, op_index); } } @@ -8006,11 +8109,53 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } } + bool was_error = false; if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { if (0 < max && max < t->EnumeratedArray.count) { error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max); + was_error = true; } else { error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed"); + was_error = true; + } + } + + // NOTE(bill): Check for missing cases when `#partial literal` is not present + if (cl->elems.count > 0 && !was_error && !is_partial) { + Type *et = base_type(index_type); + GB_ASSERT(et->kind == Type_Enum); + auto fields = et->Enum.fields; + + auto unhandled = array_make(temporary_allocator(), 0, fields.count); + + for_array(i, fields) { + Entity *f = fields[i]; + if (f->kind != Entity_Constant) { + continue; + } + ExactValue v = f->Constant.value; + auto found = map_get(&seen, hash_exact_value(v)); + if (!found) { + array_add(&unhandled, f); + } + } + + if (unhandled.count > 0) { + begin_error_block(); + defer (end_error_block()); + + if (unhandled.count == 1) { + error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); + } else { + error_no_newline(node, "Unhandled enumerated array cases: "); + for_array(i, unhandled) { + Entity *f = unhandled[i]; + error_line("\t%.*s\n", LIT(f->token.string)); + } + } + error_line("\n"); + + error_line("\tSuggestion: Was '#partial %s {...}' wanted?\n", type_to_string(index_type)); } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f9e55ab37..50b7c3233 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -697,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b return true; } - -struct TypeAndToken { - Type *type; - Token token; -}; - - -void add_constant_switch_case(CheckerContext *ctx, PtrMap *seen, Operand operand, bool use_expr = true) { - if (operand.mode != Addressing_Constant) { - return; - } - if (operand.value.kind == ExactValue_Invalid) { - return; - } - - uintptr key = hash_exact_value(operand.value); - TypeAndToken *found = map_get(seen, key); - if (found != nullptr) { - isize count = multi_map_count(seen, key); - TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); - - multi_map_get_all(seen, key, taps); - for (isize i = 0; i < count; i++) { - TypeAndToken tap = taps[i]; - if (!are_types_identical(operand.type, tap.type)) { - continue; - } - - TokenPos pos = tap.token.pos; - if (use_expr) { - gbString expr_str = expr_to_string(operand.expr); - error(operand.expr, - "Duplicate case '%s'\n" - "\tprevious case at %s", - expr_str, - token_pos_to_string(pos)); - gb_string_free(expr_str); - } else { - error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos)); - } - return; - } - } - - TypeAndToken tap = {operand.type, ast_token(operand.expr)}; - multi_map_insert(seen, key, tap); -} - void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(irs, UnrollRangeStmt, node); check_open_scope(ctx, node); @@ -1032,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { Operand b1 = rhs; check_comparison(ctx, &a1, &b1, Token_LtEq); - if (is_type_enum(x.type)) { - // TODO(bill): Fix this logic so it's fast!!! - - i64 v0 = exact_value_to_i64(lhs.value); - i64 v1 = exact_value_to_i64(rhs.value); - Operand v = {}; - v.mode = Addressing_Constant; - v.type = x.type; - v.expr = x.expr; - - Type *bt = base_type(x.type); - GB_ASSERT(bt->kind == Type_Enum); - for (i64 vi = v0; vi <= v1; vi++) { - if (upper_op != Token_GtEq && vi == v1) { - break; - } - - bool found = false; - for_array(j, bt->Enum.fields) { - Entity *f = bt->Enum.fields[j]; - GB_ASSERT(f->kind == Entity_Constant); - - i64 fv = exact_value_to_i64(f->Constant.value); - if (fv == vi) { - found = true; - break; - } - } - if (found) { - v.value = exact_value_i64(vi); - add_constant_switch_case(ctx, &seen, v); - } - } - } else { - add_constant_switch_case(ctx, &seen, lhs); - if (upper_op == Token_GtEq) { - add_constant_switch_case(ctx, &seen, rhs); - } - } + add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs); if (is_type_string(x.type)) { // NOTE(bill): Force dependency for strings here @@ -1115,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { continue; } update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type)); - add_constant_switch_case(ctx, &seen, y); + add_to_seen_map(ctx, &seen, y); } } } -- cgit v1.2.3 From dd84b61cc83c6bb3a179375f0a37adf6782b3be8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 14:07:17 +0000 Subject: Correct `add_to_seen_map` logic --- src/check_expr.cpp | 10 +++++++--- src/check_stmt.cpp | 6 +++--- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 40777df2a..b2ce6c897 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7018,7 +7018,7 @@ void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Ope Type *bt = base_type(x.type); GB_ASSERT(bt->kind == Type_Enum); for (i64 vi = v0; vi <= v1; vi++) { - if (upper_op != Token_GtEq && vi == v1) { + if (upper_op != Token_LtEq && vi == v1) { break; } @@ -7040,7 +7040,7 @@ void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Ope } } else { add_constant_switch_case(ctx, seen, lhs); - if (upper_op == Token_GtEq) { + if (upper_op == Token_LtEq) { add_constant_switch_case(ctx, seen, rhs); } } @@ -8036,7 +8036,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type is_constant = is_constant && operand.mode == Addressing_Constant; - add_to_seen_map(c, &seen, op.kind, x, x, y); + TokenKind upper_op = Token_LtEq; + if (op.kind == Token_RangeHalf) { + upper_op = Token_Lt; + } + add_to_seen_map(c, &seen, upper_op, x, x, y); } else { Operand op_index = {}; check_expr_with_type_hint(c, &op_index, fv->field, index_type); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 50b7c3233..0d18af199 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -961,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { TokenKind upper_op = Token_Invalid; switch (be->op.kind) { - case Token_Ellipsis: upper_op = Token_GtEq; break; - case Token_RangeFull: upper_op = Token_GtEq; break; - case Token_RangeHalf: upper_op = Token_Gt; break; + case Token_Ellipsis: upper_op = Token_LtEq; break; + case Token_RangeFull: upper_op = Token_LtEq; break; + case Token_RangeHalf: upper_op = Token_Lt; break; default: GB_PANIC("Invalid range operator"); break; } -- cgit v1.2.3 From 3439139b1c763fe239967bd8c90d8ccbc1e0867f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 14:34:29 +0000 Subject: Minor clean up --- src/check_expr.cpp | 5 +++-- src/check_stmt.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b2ce6c897..d90f93180 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6961,7 +6961,9 @@ struct TypeAndToken { Token token; }; -void add_constant_switch_case(CheckerContext *ctx, PtrMap *seen, Operand operand, bool use_expr = true) { +typedef PtrMap SeenMap; + +void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) { if (operand.mode != Addressing_Constant) { return; } @@ -7002,7 +7004,6 @@ void add_constant_switch_case(CheckerContext *ctx, PtrMap multi_map_insert(seen, key, tap); } -typedef PtrMap SeenMap; void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) { if (is_type_enum(x.type)) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 0d18af199..2d85db82c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -921,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } } - PtrMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue + SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue map_init(&seen, heap_allocator()); defer (map_destroy(&seen)); -- cgit v1.2.3 From a4308e7246a995c745b120debbb61be9e1f19a38 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 14:45:59 +0000 Subject: Improve union variant assignment determination --- src/check_expr.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d90f93180..d6b454bf4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -673,6 +673,42 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type return 1; } } + + // TODO(bill): Determine which rule is a better on in practice + #if 1 + if (dst->Union.variants.count == 1) { + Type *vt = dst->Union.variants[0]; + i64 score = check_distance_between_types(c, operand, vt); + if (score >= 0) { + return score+2; + } + } + #else + // NOTE(bill): check to see you can assign to it with one of the variants? + i64 prev_lowest_score = -1; + i64 lowest_score = -1; + for_array(i, dst->Union.variants) { + Type *vt = dst->Union.variants[i]; + i64 score = check_distance_between_types(c, operand, vt); + if (score >= 0) { + if (lowest_score < 0) { + lowest_score = score; + } else { + if (prev_lowest_score < 0) { + prev_lowest_score = lowest_score; + } else { + prev_lowest_score = gb_min(prev_lowest_score, lowest_score); + } + lowest_score = gb_min(lowest_score, score); + } + } + } + if (lowest_score >= 0) { + if (prev_lowest_score != lowest_score) { // remove possible ambiguities + return lowest_score+2; + } + } + #endif } if (is_type_relative_pointer(dst)) { -- cgit v1.2.3 From 23c3573c307fc9b1c7aa2af2b445090543fd60d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 14:56:06 +0000 Subject: Minor correction to error message suggestion --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d6b454bf4..4184d5b30 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8196,7 +8196,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } error_line("\n"); - error_line("\tSuggestion: Was '#partial %s {...}' wanted?\n", type_to_string(index_type)); + error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); } } -- cgit v1.2.3 From 67ce0ec29f55621a36ddc1bde83f23a51c9ce355 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 14:58:13 +0000 Subject: Improve printing for unhandled cases by adding a new line before the cases --- src/check_expr.cpp | 2 +- src/check_stmt.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4184d5b30..4664d2244 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8188,7 +8188,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (unhandled.count == 1) { error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); } else { - error_no_newline(node, "Unhandled enumerated array cases: "); + error(node, "Unhandled enumerated array cases:"); for_array(i, unhandled) { Entity *f = unhandled[i]; error_line("\t%.*s\n", LIT(f->token.string)); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2d85db82c..7cae1893f 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1065,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); } else { - error_no_newline(node, "Unhandled switch cases: "); + error(node, "Unhandled switch cases:"); for_array(i, unhandled) { Entity *f = unhandled[i]; error_line("\t%.*s\n", LIT(f->token.string)); -- cgit v1.2.3 From c6ab8f82c88f7d5470fa2fb0459d09ba8a67a287 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 15:17:47 +0000 Subject: Code refactor to aid development --- src/check_expr.cpp | 3334 +++++++++++++++++++++++++++------------------------- 1 file changed, 1702 insertions(+), 1632 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4664d2244..fb58839bc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7086,546 +7086,573 @@ void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) { add_constant_switch_case(ctx, seen, x); } - -ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { - u32 prev_state_flags = c->state_flags; - defer (c->state_flags = prev_state_flags); - if (node->state_flags != 0) { - u32 in = node->state_flags; - u32 out = c->state_flags; - - if (in & StateFlag_no_bounds_check) { - out |= StateFlag_no_bounds_check; - out &= ~StateFlag_bounds_check; - } else if (in & StateFlag_bounds_check) { - out |= StateFlag_bounds_check; - out &= ~StateFlag_no_bounds_check; +ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(bd, BasicDirective, node); + + ExprKind kind = Expr_Expr; + + o->mode = Addressing_Constant; + String name = bd->name.string; + if (name == "file") { + o->type = t_untyped_string; + o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); + } else if (name == "line") { + o->type = t_untyped_integer; + o->value = exact_value_i64(bd->token.pos.line); + } else if (name == "procedure") { + if (c->curr_proc_decl == nullptr) { + error(node, "#procedure may only be used within procedures"); + o->type = t_untyped_string; + o->value = exact_value_string(str_lit("")); + } else { + o->type = t_untyped_string; + o->value = exact_value_string(c->proc_name); } - - if (in & StateFlag_no_type_assert) { - out |= StateFlag_no_type_assert; - out &= ~StateFlag_type_assert; - } else if (in & StateFlag_type_assert) { - out |= StateFlag_type_assert; - out &= ~StateFlag_no_type_assert; + } else if (name == "caller_location") { + init_core_source_code_location(c->checker); + error(node, "#caller_location may only be used as a default argument parameter"); + o->type = t_source_code_location; + o->mode = Addressing_Value; + } else { + if (name == "location") { + init_core_source_code_location(c->checker); + error(node, "'#%.*s' must be used in a call expression", LIT(name)); + o->type = t_source_code_location; + o->mode = Addressing_Value; + } else if ( + name == "assert" || + name == "defined" || + name == "config" || + name == "load" || + name == "load_hash" || + name == "load_or" + ) { + error(node, "'#%.*s' must be used as a call", LIT(name)); + o->type = t_invalid; + o->mode = Addressing_Invalid; + } else { + error(node, "Unknown directive: #%.*s", LIT(name)); + o->type = t_invalid; + o->mode = Addressing_Invalid; } - c->state_flags = out; } + return kind; +} - ExprKind kind = Expr_Stmt; +ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + Operand cond = {Addressing_Invalid}; + ast_node(te, TernaryIfExpr, node); + check_expr(c, &cond, te->cond); + node->viral_state_flags |= te->cond->viral_state_flags; - o->mode = Addressing_Invalid; - o->type = t_invalid; + if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { + error(te->cond, "Non-boolean condition in ternary if expression"); + } - switch (node->kind) { - default: + Operand x = {Addressing_Invalid}; + Operand y = {Addressing_Invalid}; + check_expr_or_type(c, &x, te->x, type_hint); + node->viral_state_flags |= te->x->viral_state_flags; + + if (te->y != nullptr) { + check_expr_or_type(c, &y, te->y, type_hint); + node->viral_state_flags |= te->y->viral_state_flags; + } else { + error(node, "A ternary expression must have an else clause"); return kind; + } - case_ast_node(be, BadExpr, node) + if (x.type == nullptr || x.type == t_invalid || + y.type == nullptr || y.type == t_invalid) { return kind; - case_end; + } - case_ast_node(i, Implicit, node) - switch (i->kind) { - case Token_context: - { - if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) { - error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl); - return kind; - } - if (unparen_expr(c->assignment_lhs_hint) == node) { - c->scope->flags |= ScopeFlag_ContextDefined; - } + convert_to_typed(c, &x, y.type); + if (x.mode == Addressing_Invalid) { + return kind; + } + convert_to_typed(c, &y, x.type); + if (y.mode == Addressing_Invalid) { + x.mode = Addressing_Invalid; + return kind; + } - if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { - error(node, "'context' has not been defined within this scope"); - // Continue with value - } + if (!ternary_compare_types(x.type, y.type)) { + gbString its = type_to_string(x.type); + gbString ets = type_to_string(y.type); + error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets); + gb_string_free(ets); + gb_string_free(its); + return kind; + } - init_core_context(c->checker); - o->mode = Addressing_Context; - o->type = t_context; - } - break; + o->type = x.type; + if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) { + o->type = y.type; + } - default: - error(node, "Illegal implicit name '%.*s'", LIT(i->string)); + o->mode = Addressing_Value; + o->expr = node; + if (type_hint != nullptr && is_type_untyped(o->type)) { + if (check_cast_internal(c, &x, type_hint) && + check_cast_internal(c, &y, type_hint)) { + convert_to_typed(c, o, type_hint); + update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); + } + } + return kind; +} + +ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + Operand cond = {}; + ast_node(te, TernaryWhenExpr, node); + check_expr(c, &cond, te->cond); + node->viral_state_flags |= te->cond->viral_state_flags; + + if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) { + error(te->cond, "Expected a constant boolean condition in ternary when expression"); + return kind; + } + + if (cond.value.value_bool) { + check_expr_or_type(c, o, te->x, type_hint); + node->viral_state_flags |= te->x->viral_state_flags; + } else { + if (te->y != nullptr) { + check_expr_or_type(c, o, te->y, type_hint); + node->viral_state_flags |= te->y->viral_state_flags; + } else { + error(node, "A ternary when expression must have an else clause"); return kind; } - case_end; + } + return kind; +} - case_ast_node(i, Ident, node); - check_ident(c, o, node, nullptr, type_hint, false); - case_end; +ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(oe, OrElseExpr, node); - case_ast_node(u, Undef, node); + String name = oe->token.string; + Ast *arg = oe->x; + Ast *default_value = oe->y; + + Operand x = {}; + Operand y = {}; + check_multi_expr_with_type_hint(c, &x, arg, type_hint); + if (x.mode == Addressing_Invalid) { o->mode = Addressing_Value; - o->type = t_untyped_undef; - case_end; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } + check_multi_expr_with_type_hint(c, &y, default_value, x.type); + error_operand_no_value(&y); + if (y.mode == Addressing_Invalid) { + o->mode = Addressing_Value; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } - case_ast_node(bl, BasicLit, node); - Type *t = t_invalid; - switch (node->tav.value.kind) { - case ExactValue_String: t = t_untyped_string; break; - case ExactValue_Float: t = t_untyped_float; break; - case ExactValue_Complex: t = t_untyped_complex; break; - case ExactValue_Quaternion: t = t_untyped_quaternion; break; - case ExactValue_Integer: - t = t_untyped_integer; - if (bl->token.kind == Token_Rune) { - t = t_untyped_rune; - } - break; - default: - GB_PANIC("Unhandled value type for basic literal"); - break; - } + Type *left_type = nullptr; + Type *right_type = nullptr; + check_or_else_split_types(c, &x, name, &left_type, &right_type); + add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value); - o->mode = Addressing_Constant; - o->type = t; - o->value = node->tav.value; - case_end; + if (left_type != nullptr) { + check_assignment(c, &y, left_type, name); + } else { + check_or_else_expr_no_value_error(c, name, x, type_hint); + } - case_ast_node(bd, BasicDirective, node); - o->mode = Addressing_Constant; - String name = bd->name.string; - if (name == "file") { - o->type = t_untyped_string; - o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); - } else if (name == "line") { - o->type = t_untyped_integer; - o->value = exact_value_i64(bd->token.pos.line); - } else if (name == "procedure") { - if (c->curr_proc_decl == nullptr) { - error(node, "#procedure may only be used within procedures"); - o->type = t_untyped_string; - o->value = exact_value_string(str_lit("")); - } else { - o->type = t_untyped_string; - o->value = exact_value_string(c->proc_name); - } - } else if (name == "caller_location") { - init_core_source_code_location(c->checker); - error(node, "#caller_location may only be used as a default argument parameter"); - o->type = t_source_code_location; - o->mode = Addressing_Value; - } else { - if (name == "location") { - init_core_source_code_location(c->checker); - error(node, "'#%.*s' must be used in a call expression", LIT(name)); - o->type = t_source_code_location; - o->mode = Addressing_Value; - } else if ( - name == "assert" || - name == "defined" || - name == "config" || - name == "load" || - name == "load_hash" || - name == "load_or" - ) { - error(node, "'#%.*s' must be used as a call", LIT(name)); - o->type = t_invalid; - o->mode = Addressing_Invalid; - } else { - error(node, "Unknown directive: #%.*s", LIT(name)); - o->type = t_invalid; - o->mode = Addressing_Invalid; - } - - } - case_end; + if (left_type == nullptr) { + left_type = t_invalid; + } + o->mode = Addressing_Value; + o->type = left_type; + o->expr = node; + return Expr_Expr; +} - case_ast_node(pg, ProcGroup, node); - error(node, "Illegal use of a procedure group"); - o->mode = Addressing_Invalid; - case_end; +ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(re, OrReturnExpr, node); - case_ast_node(pl, ProcLit, node); - CheckerContext ctx = *c; + String name = re->token.string; + Operand x = {}; + check_multi_expr_with_type_hint(c, &x, re->expr, type_hint); + if (x.mode == Addressing_Invalid) { + o->mode = Addressing_Value; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } - DeclInfo *decl = nullptr; - Type *type = alloc_type(Type_Proc); - check_open_scope(&ctx, pl->type); - { - decl = make_decl_info(ctx.scope, ctx.decl); - decl->proc_lit = node; - ctx.decl = decl; - defer (ctx.decl = ctx.decl->parent); + Type *left_type = nullptr; + Type *right_type = nullptr; + check_or_return_split_types(c, &x, name, &left_type, &right_type); + add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value); - if (pl->tags != 0) { - error(node, "A procedure literal cannot have tags"); - pl->tags = 0; // TODO(bill): Should I zero this?! - } + if (right_type == nullptr) { + check_or_else_expr_no_value_error(c, name, x, type_hint); + } else { + Type *proc_type = base_type(c->curr_proc_sig); + GB_ASSERT(proc_type->kind == Type_Proc); + Type *result_type = proc_type->Proc.results; + if (result_type == nullptr) { + error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name)); + } else { + GB_ASSERT(result_type->kind == Type_Tuple); - check_procedure_type(&ctx, type, pl->type); - if (!is_type_proc(type)) { - gbString str = expr_to_string(node); - error(node, "Invalid procedure literal '%s'", str); - gb_string_free(str); - check_close_scope(&ctx); - return kind; - } + auto const &vars = result_type->Tuple.variables; + Type *end_type = vars[vars.count-1]->type; - if (pl->body == nullptr) { - error(node, "A procedure literal must have a body"); - return kind; + if (vars.count > 1) { + if (!proc_type->Proc.has_named_results) { + error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name)); + } } - pl->decl = decl; - check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags); + Operand rhs = {}; + rhs.type = right_type; + rhs.mode = Addressing_Value; + + // TODO(bill): better error message + if (!check_is_assignable_to(c, &rhs, end_type)) { + gbString a = type_to_string(right_type); + gbString b = type_to_string(end_type); + gbString ret_type = type_to_string(result_type); + error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name)); + if (vars.count == 1) { + error_line("\tProcedure return value type: %s\n", ret_type); + } else { + error_line("\tProcedure return value types: (%s)\n", ret_type); + } + gb_string_free(ret_type); + gb_string_free(b); + gb_string_free(a); + } } - check_close_scope(&ctx); + } + o->expr = node; + o->type = left_type; + if (left_type != nullptr) { o->mode = Addressing_Value; - o->type = type; - case_end; + } else { + o->mode = Addressing_NoValue; + } - case_ast_node(te, TernaryIfExpr, node); - Operand cond = {Addressing_Invalid}; - check_expr(c, &cond, te->cond); - node->viral_state_flags |= te->cond->viral_state_flags; + if (c->curr_proc_sig == nullptr) { + error(node, "'%.*s' can only be used within a procedure", LIT(name)); + } - if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { - error(te->cond, "Non-boolean condition in ternary if expression"); - } + if (c->in_defer) { + error(node, "'or_return' cannot be used within a defer statement"); + } - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; - check_expr_or_type(c, &x, te->x, type_hint); - node->viral_state_flags |= te->x->viral_state_flags; + return Expr_Expr; +} - if (te->y != nullptr) { - check_expr_or_type(c, &y, te->y, type_hint); - node->viral_state_flags |= te->y->viral_state_flags; - } else { - error(node, "A ternary expression must have an else clause"); - return kind; +ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + ast_node(cl, CompoundLit, node); + + Type *type = type_hint; + if (type != nullptr && is_type_untyped(type)) { + type = nullptr; + } + bool is_to_be_determined_array_count = false; + bool is_constant = true; + if (cl->type != nullptr) { + type = nullptr; + + // [?]Type + if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { + Ast *count = cl->type->ArrayType.count; + if (count->kind == Ast_UnaryExpr && + count->UnaryExpr.op.kind == Token_Question) { + type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); + is_to_be_determined_array_count = true; + } + if (cl->elems.count > 0) { + if (cl->type->ArrayType.tag != nullptr) { + Ast *tag = cl->type->ArrayType.tag; + GB_ASSERT(tag->kind == Ast_BasicDirective); + String name = tag->BasicDirective.name.string; + if (name == "soa") { + error(node, "#soa arrays are not supported for compound literals"); + return kind; + } + } + } } - - if (x.type == nullptr || x.type == t_invalid || - y.type == nullptr || y.type == t_invalid) { - return kind; + if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { + if (cl->elems.count > 0) { + Ast *tag = cl->type->DynamicArrayType.tag; + GB_ASSERT(tag->kind == Ast_BasicDirective); + String name = tag->BasicDirective.name.string; + if (name == "soa") { + error(node, "#soa arrays are not supported for compound literals"); + return kind; + } + } } - convert_to_typed(c, &x, y.type); - if (x.mode == Addressing_Invalid) { - return kind; - } - convert_to_typed(c, &y, x.type); - if (y.mode == Addressing_Invalid) { - x.mode = Addressing_Invalid; - return kind; + if (type == nullptr) { + type = check_type(c, cl->type); } + } - if (!ternary_compare_types(x.type, y.type)) { - gbString its = type_to_string(x.type); - gbString ets = type_to_string(y.type); - error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets); - gb_string_free(ets); - gb_string_free(its); - return kind; - } + if (type == nullptr) { + error(node, "Missing type in compound literal"); + return kind; + } - o->type = x.type; - if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) { - o->type = y.type; - } - o->mode = Addressing_Value; + Type *t = base_type(type); + if (is_type_polymorphic(t)) { + gbString str = type_to_string(type); + error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str); o->expr = node; - if (type_hint != nullptr && is_type_untyped(o->type)) { - if (check_cast_internal(c, &x, type_hint) && - check_cast_internal(c, &y, type_hint)) { - convert_to_typed(c, o, type_hint); - update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); - } - } - case_end; - - case_ast_node(te, TernaryWhenExpr, node); - Operand cond = {}; - check_expr(c, &cond, te->cond); - node->viral_state_flags |= te->cond->viral_state_flags; - - if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) { - error(te->cond, "Expected a constant boolean condition in ternary when expression"); - return kind; - } - - if (cond.value.value_bool) { - check_expr_or_type(c, o, te->x, type_hint); - node->viral_state_flags |= te->x->viral_state_flags; - } else { - if (te->y != nullptr) { - check_expr_or_type(c, o, te->y, type_hint); - node->viral_state_flags |= te->y->viral_state_flags; - } else { - error(node, "A ternary when expression must have an else clause"); - return kind; - } - } - case_end; - - case_ast_node(oe, OrElseExpr, node); - String name = oe->token.string; - Ast *arg = oe->x; - Ast *default_value = oe->y; + o->type = type; + gb_string_free(str); + return kind; + } - Operand x = {}; - Operand y = {}; - check_multi_expr_with_type_hint(c, &x, arg, type_hint); - if (x.mode == Addressing_Invalid) { - o->mode = Addressing_Value; - o->type = t_invalid; - o->expr = node; - return Expr_Expr; - } - check_multi_expr_with_type_hint(c, &y, default_value, x.type); - error_operand_no_value(&y); - if (y.mode == Addressing_Invalid) { - o->mode = Addressing_Value; - o->type = t_invalid; - o->expr = node; - return Expr_Expr; + switch (t->kind) { + case Type_Struct: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init } + if (t->Struct.is_raw_union) { + if (cl->elems.count > 0) { + // NOTE: unions cannot be constant + is_constant = false; - Type *left_type = nullptr; - Type *right_type = nullptr; - check_or_else_split_types(c, &x, name, &left_type, &right_type); - add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value); + if (cl->elems[0]->kind != Ast_FieldValue) { + gbString type_str = type_to_string(type); + error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str); + gb_string_free(type_str); + } else { + if (cl->elems.count != 1) { + gbString type_str = type_to_string(type); + error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); + gb_string_free(type_str); + } else { + Ast *elem = cl->elems[0]; + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in structure literal", expr_str); + gb_string_free(expr_str); + break; + } - if (left_type != nullptr) { - check_assignment(c, &y, left_type, name); - } else { - check_or_else_expr_no_value_error(c, name, x, type_hint); - } + String name = fv->field->Ident.token.string; - if (left_type == nullptr) { - left_type = t_invalid; - } - o->mode = Addressing_Value; - o->type = left_type; - o->expr = node; - return Expr_Expr; - case_end; + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + bool is_unknown = sel.entity == nullptr; + if (is_unknown) { + error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); + break; + } - case_ast_node(re, OrReturnExpr, node); - String name = re->token.string; - Operand x = {}; - check_multi_expr_with_type_hint(c, &x, re->expr, type_hint); - if (x.mode == Addressing_Invalid) { - o->mode = Addressing_Value; - o->type = t_invalid; - o->expr = node; - return Expr_Expr; - } + if (sel.index.count > 1) { + error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); + break; + } - Type *left_type = nullptr; - Type *right_type = nullptr; - check_or_return_split_types(c, &x, name, &left_type, &right_type); - add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value); + Entity *field = t->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); - if (right_type == nullptr) { - check_or_else_expr_no_value_error(c, name, x, type_hint); - } else { - Type *proc_type = base_type(c->curr_proc_sig); - GB_ASSERT(proc_type->kind == Type_Proc); - Type *result_type = proc_type->Proc.results; - if (result_type == nullptr) { - error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name)); - } else { - GB_ASSERT(result_type->kind == Type_Tuple); + Operand o = {}; + check_expr_or_type(c, &o, fv->value, field->type); - auto const &vars = result_type->Tuple.variables; - Type *end_type = vars[vars.count-1]->type; - if (vars.count > 1) { - if (!proc_type->Proc.has_named_results) { - error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name)); + check_assignment(c, &o, field->type, str_lit("structure literal")); } - } - Operand rhs = {}; - rhs.type = right_type; - rhs.mode = Addressing_Value; - - // TODO(bill): better error message - if (!check_is_assignable_to(c, &rhs, end_type)) { - gbString a = type_to_string(right_type); - gbString b = type_to_string(end_type); - gbString ret_type = type_to_string(result_type); - error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name)); - if (vars.count == 1) { - error_line("\tProcedure return value type: %s\n", ret_type); - } else { - error_line("\tProcedure return value types: (%s)\n", ret_type); - } - gb_string_free(ret_type); - gb_string_free(b); - gb_string_free(a); } } + break; } - o->expr = node; - o->type = left_type; - if (left_type != nullptr) { - o->mode = Addressing_Value; - } else { - o->mode = Addressing_NoValue; - } - if (c->curr_proc_sig == nullptr) { - error(node, "'%.*s' can only be used within a procedure", LIT(name)); - } - - if (c->in_defer) { - error(node, "'or_return' cannot be used within a defer statement"); + isize field_count = t->Struct.fields.count; + isize min_field_count = t->Struct.fields.count; + for (isize i = min_field_count-1; i >= 0; i--) { + Entity *e = t->Struct.fields[i]; + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + min_field_count--; + } else { + break; + } } - return Expr_Expr; - case_end; + if (cl->elems[0]->kind == Ast_FieldValue) { + bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); - case_ast_node(cl, CompoundLit, node); - Type *type = type_hint; - if (type != nullptr && is_type_untyped(type)) { - type = nullptr; - } - bool is_to_be_determined_array_count = false; - bool is_constant = true; - if (cl->type != nullptr) { - type = nullptr; - - // [?]Type - if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { - Ast *count = cl->type->ArrayType.count; - if (count->kind == Ast_UnaryExpr && - count->UnaryExpr.op.kind == Token_Question) { - type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); - is_to_be_determined_array_count = true; - } - if (cl->elems.count > 0) { - if (cl->type->ArrayType.tag != nullptr) { - Ast *tag = cl->type->ArrayType.tag; - GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name.string; - if (name == "soa") { - error(node, "#soa arrays are not supported for compound literals"); - return kind; - } - } + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; } - } - if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { - if (cl->elems.count > 0) { - Ast *tag = cl->type->DynamicArrayType.tag; - GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name.string; - if (name == "soa") { - error(node, "#soa arrays are not supported for compound literals"); - return kind; - } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in structure literal", expr_str); + gb_string_free(expr_str); + continue; } - } + String name = fv->field->Ident.token.string; - if (type == nullptr) { - type = check_type(c, cl->type); - } - } + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + bool is_unknown = sel.entity == nullptr; + if (is_unknown) { + error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); + continue; + } - if (type == nullptr) { - error(node, "Missing type in compound literal"); - return kind; - } + if (sel.index.count > 1) { + error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); + continue; + } + Entity *field = t->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); - Type *t = base_type(type); - if (is_type_polymorphic(t)) { - gbString str = type_to_string(type); - error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str); - o->expr = node; - o->type = type; - gb_string_free(str); - return kind; - } + if (fields_visited[sel.index[0]]) { + error(elem, "Duplicate field '%.*s' in structure literal", LIT(name)); + continue; + } + fields_visited[sel.index[0]] = true; - switch (t->kind) { - case Type_Struct: { - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - if (t->Struct.is_raw_union) { - if (cl->elems.count > 0) { - // NOTE: unions cannot be constant + Operand o = {}; + check_expr_or_type(c, &o, fv->value, field->type); + + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } - if (cl->elems[0]->kind != Ast_FieldValue) { - gbString type_str = type_to_string(type); - error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str); - gb_string_free(type_str); - } else { - if (cl->elems.count != 1) { - gbString type_str = type_to_string(type); - error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); - gb_string_free(type_str); - } else { - Ast *elem = cl->elems[0]; - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - break; - } + check_assignment(c, &o, field->type, str_lit("structure literal")); + } + } else { + bool seen_field_value = false; + + for_array(index, cl->elems) { + Entity *field = nullptr; + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + seen_field_value = true; + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } else if (seen_field_value) { + error(elem, "Value elements cannot be used after a 'field = value'"); + continue; + } + if (index >= field_count) { + error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count); + break; + } - String name = fv->field->Ident.token.string; + if (field == nullptr) { + field = t->Struct.fields[index]; + } - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - break; - } + Operand o = {}; + check_expr_or_type(c, &o, elem, field->type); - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - break; - } + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); + check_assignment(c, &o, field->type, str_lit("structure literal")); + } + if (cl->elems.count < field_count) { + if (min_field_count < field_count) { + if (cl->elems.count < min_field_count) { + error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count); + } + } else { + error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); + } + } + } - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); + break; + } + case Type_Slice: + case Type_Array: + case Type_DynamicArray: + case Type_SimdVector: + case Type_Matrix: + { + Type *elem_type = nullptr; + String context_name = {}; + i64 max_type_count = -1; + if (t->kind == Type_Slice) { + elem_type = t->Slice.elem; + context_name = str_lit("slice literal"); + } else if (t->kind == Type_Array) { + elem_type = t->Array.elem; + context_name = str_lit("array literal"); + if (!is_to_be_determined_array_count) { + max_type_count = t->Array.count; + } + } else if (t->kind == Type_DynamicArray) { + elem_type = t->DynamicArray.elem; + context_name = str_lit("dynamic array literal"); + is_constant = false; - check_assignment(c, &o, field->type, str_lit("structure literal")); - } + if (!build_context.no_dynamic_literals) { + add_package_dependency(c, "runtime", "__dynamic_array_reserve"); + add_package_dependency(c, "runtime", "__dynamic_array_append"); + } + } else if (t->kind == Type_SimdVector) { + elem_type = t->SimdVector.elem; + context_name = str_lit("simd vector literal"); + max_type_count = t->SimdVector.count; + } else if (t->kind == Type_Matrix) { + elem_type = t->Matrix.elem; + context_name = str_lit("matrix literal"); + max_type_count = t->Matrix.row_count*t->Matrix.column_count; + } else { + GB_PANIC("unreachable"); + } - } - } - break; - } + i64 max = 0; - isize field_count = t->Struct.fields.count; - isize min_field_count = t->Struct.fields.count; - for (isize i = min_field_count-1; i >= 0; i--) { - Entity *e = t->Struct.fields[i]; - GB_ASSERT(e->kind == Entity_Variable); - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - min_field_count--; - } else { - break; - } - } + Type *bet = base_type(elem_type); + if (!elem_type_can_be_constant(bet)) { + is_constant = false; + } - if (cl->elems[0]->kind == Ast_FieldValue) { - bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); + if (bet == t_invalid) { + break; + } + + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + if (is_type_simd_vector(t)) { + error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); + } else { + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -7634,954 +7661,1419 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type continue; } ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - continue; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - continue; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - if (fields_visited[sel.index[0]]) { - error(elem, "Duplicate field '%.*s' in structure literal", LIT(name)); - continue; - } + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; - fields_visited[sel.index[0]] = true; + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { // ..< (exclusive) + hi -= 1; + } else { // .. (inclusive) + max_index += 1; + } - check_assignment(c, &o, field->type, str_lit("structure literal")); - } - } else { - bool seen_field_value = false; + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } - for_array(index, cl->elems) { - Entity *field = nullptr; - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - seen_field_value = true; - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } else if (seen_field_value) { - error(elem, "Value elements cannot be used after a 'field = value'"); - continue; - } - if (index >= field_count) { - error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count); - break; - } - if (field == nullptr) { - field = t->Struct.fields[index]; - } + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } - Operand o = {}; - check_expr_or_type(c, &o, elem, field->type); + if (max < hi) { + max = max_index; + } - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); - check_assignment(c, &o, field->type, str_lit("structure literal")); - } - if (cl->elems.count < field_count) { - if (min_field_count < field_count) { - if (cl->elems.count < min_field_count) { - error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count); - } + is_constant = is_constant && operand.mode == Addressing_Constant; } else { - error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); - } - } - } + Operand op_index = {}; + check_expr(c, &op_index, fv->field); - break; - } + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } + // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value); - case Type_Slice: - case Type_Array: - case Type_DynamicArray: - case Type_SimdVector: - case Type_Matrix: - { - Type *elem_type = nullptr; - String context_name = {}; - i64 max_type_count = -1; - if (t->kind == Type_Slice) { - elem_type = t->Slice.elem; - context_name = str_lit("slice literal"); - } else if (t->kind == Type_Array) { - elem_type = t->Array.elem; - context_name = str_lit("array literal"); - if (!is_to_be_determined_array_count) { - max_type_count = t->Array.count; - } - } else if (t->kind == Type_DynamicArray) { - elem_type = t->DynamicArray.elem; - context_name = str_lit("dynamic array literal"); - is_constant = false; + i64 index = exact_value_to_i64(op_index.value); - if (!build_context.no_dynamic_literals) { - add_package_dependency(c, "runtime", "__dynamic_array_reserve"); - add_package_dependency(c, "runtime", "__dynamic_array_append"); - } - } else if (t->kind == Type_SimdVector) { - elem_type = t->SimdVector.elem; - context_name = str_lit("simd vector literal"); - max_type_count = t->SimdVector.count; - } else if (t->kind == Type_Matrix) { - elem_type = t->Matrix.elem; - context_name = str_lit("matrix literal"); - max_type_count = t->Matrix.row_count*t->Matrix.column_count; - } else { - GB_PANIC("unreachable"); - } + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } - i64 max = 0; + if (max < index+1) { + max = index+1; + } - Type *bet = base_type(elem_type); - if (!elem_type_can_be_constant(bet)) { - is_constant = false; + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + } + + cl->max_count = max; } - if (bet == t_invalid) { - break; + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; } - if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { - if (is_type_simd_vector(t)) { - error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); - } else { - RangeCache rc = range_cache_make(heap_allocator()); - defer (range_cache_destroy(&rc)); + if (max < index) { + max = index; + } + } - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - if (is_ast_range(fv->field)) { - Token op = fv->field->BinaryExpr.op; + if (t->kind == Type_Array) { + if (is_to_be_determined_array_count) { + t->Array.count = max; + } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Array.count) { + error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } + } + } - Operand x = {}; - Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr); - if (!ok) { - continue; - } - if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { - error(x.expr, "Expected a constant integer as an array field"); - continue; - } - if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { - error(y.expr, "Expected a constant integer as an array field"); - continue; - } + if (t->kind == Type_SimdVector) { + if (!is_constant) { + error(node, "Expected all constant elements for a simd vector"); + } + } - i64 lo = exact_value_to_i64(x.value); - i64 hi = exact_value_to_i64(y.value); - i64 max_index = hi; - if (op.kind == Token_RangeHalf) { // ..< (exclusive) - hi -= 1; - } else { // .. (inclusive) - max_index += 1; - } - bool new_range = range_cache_add_range(&rc, lo, hi); - if (!new_range) { - error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); - continue; - } + if (t->kind == Type_DynamicArray) { + if (build_context.no_dynamic_literals && cl->elems.count) { + error(node, "Compound literals of dynamic types have been disabled"); + } + } + if (t->kind == Type_Matrix) { + if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < max_type_count) { + error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max); + } + } + } - if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); - continue; - } - if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); - continue; - } + break; + } - if (max < hi) { - max = max_index; - } + case Type_EnumeratedArray: + { + Type *elem_type = t->EnumeratedArray.elem; + Type *index_type = t->EnumeratedArray.index; + String context_name = str_lit("enumerated array literal"); + i64 max_type_count = t->EnumeratedArray.count; - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + gbString index_type_str = type_to_string(index_type); + defer (gb_string_free(index_type_str)); - is_constant = is_constant && operand.mode == Addressing_Constant; - } else { - Operand op_index = {}; - check_expr(c, &op_index, fv->field); + i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value); + i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value); - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } - // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value); + String total_lo_string = {}; + String total_hi_string = {}; + GB_ASSERT(is_type_enum(index_type)); + { + Type *bt = base_type(index_type); + GB_ASSERT(bt->kind == Type_Enum); + for_array(i, bt->Enum.fields) { + Entity *f = bt->Enum.fields[i]; + if (f->kind != Entity_Constant) { + continue; + } + if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) { + total_lo_string = f->token.string; + } + if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) { + total_hi_string = f->token.string; + } + if (total_lo_string.len != 0 && total_hi_string.len != 0) { + break; + } + } + } - i64 index = exact_value_to_i64(op_index.value); + i64 max = 0; - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } + Type *bet = base_type(elem_type); + if (!elem_type_can_be_constant(bet)) { + is_constant = false; + } - bool new_index = range_cache_add_index(&rc, index); - if (!new_index) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } + if (bet == t_invalid) { + break; + } + bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial"); - if (max < index+1) { - max = index+1; - } + SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue + map_init(&seen, heap_allocator()); + defer (map_destroy(&seen)); - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); - is_constant = is_constant && operand.mode == Addressing_Constant; - } + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; + + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { + error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); + continue; } - cl->max_count = max; - } + if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { + error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); + continue; + } - } else { - isize index = 0; - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + gbString lo_str = expr_to_string(x.expr); + gbString hi_str = expr_to_string(y.expr); + error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name)); + gb_string_free(hi_str); + gb_string_free(lo_str); continue; } - if (e->kind == Ast_FieldValue) { - error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + + // NOTE(bill): These are sanity checks for invalid enum values + if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) { + gbString lo_str = expr_to_string(x.expr); + error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); + gb_string_free(lo_str); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) { + gbString hi_str = expr_to_string(y.expr); + error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); + gb_string_free(hi_str); continue; } - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + if (max < hi) { + max = max_index; } Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); + check_expr_with_type_hint(c, &operand, fv->value, elem_type); check_assignment(c, &operand, elem_type, context_name); is_constant = is_constant && operand.mode == Addressing_Constant; - } - if (max < index) { - max = index; - } - } + TokenKind upper_op = Token_LtEq; + if (op.kind == Token_RangeHalf) { + upper_op = Token_Lt; + } + add_to_seen_map(c, &seen, upper_op, x, x, y); + } else { + Operand op_index = {}; + check_expr_with_type_hint(c, &op_index, fv->field, index_type); + + if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) { + error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); + continue; + } + i64 index = exact_value_to_i64(op_index.value); - if (t->kind == Type_Array) { - if (is_to_be_determined_array_count) { - t->Array.count = max; - } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { - if (0 < max && max < t->Array.count) { - error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + if (max_type_count >= 0 && (index < total_lo || index > total_hi)) { + gbString idx_str = expr_to_string(op_index.expr); + error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); + gb_string_free(idx_str); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + gbString idx_str = expr_to_string(op_index.expr); + error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name)); + gb_string_free(idx_str); + continue; } - } - } + if (max < index+1) { + max = index+1; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); - if (t->kind == Type_SimdVector) { - if (!is_constant) { - error(node, "Expected all constant elements for a simd vector"); + is_constant = is_constant && operand.mode == Addressing_Constant; + + add_to_seen_map(c, &seen, op_index); } } + cl->max_count = max; + + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } - if (t->kind == Type_DynamicArray) { - if (build_context.no_dynamic_literals && cl->elems.count) { - error(node, "Compound literals of dynamic types have been disabled"); + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; } - } - if (t->kind == Type_Matrix) { - if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { - if (0 < max && max < max_type_count) { - error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max); - } + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; } - break; + if (max < index) { + max = index; + } } - case Type_EnumeratedArray: - { - Type *elem_type = t->EnumeratedArray.elem; - Type *index_type = t->EnumeratedArray.index; - String context_name = str_lit("enumerated array literal"); - i64 max_type_count = t->EnumeratedArray.count; + bool was_error = false; + if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->EnumeratedArray.count) { + error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max); + was_error = true; + } else { + error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed"); + was_error = true; + } + } - gbString index_type_str = type_to_string(index_type); - defer (gb_string_free(index_type_str)); + // NOTE(bill): Check for missing cases when `#partial literal` is not present + if (cl->elems.count > 0 && !was_error && !is_partial) { + Type *et = base_type(index_type); + GB_ASSERT(et->kind == Type_Enum); + auto fields = et->Enum.fields; - i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value); - i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value); + auto unhandled = array_make(temporary_allocator(), 0, fields.count); - String total_lo_string = {}; - String total_hi_string = {}; - GB_ASSERT(is_type_enum(index_type)); - { - Type *bt = base_type(index_type); - GB_ASSERT(bt->kind == Type_Enum); - for_array(i, bt->Enum.fields) { - Entity *f = bt->Enum.fields[i]; - if (f->kind != Entity_Constant) { - continue; - } - if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) { - total_lo_string = f->token.string; - } - if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) { - total_hi_string = f->token.string; - } - if (total_lo_string.len != 0 && total_hi_string.len != 0) { - break; - } + for_array(i, fields) { + Entity *f = fields[i]; + if (f->kind != Entity_Constant) { + continue; + } + ExactValue v = f->Constant.value; + auto found = map_get(&seen, hash_exact_value(v)); + if (!found) { + array_add(&unhandled, f); } } - i64 max = 0; + if (unhandled.count > 0) { + begin_error_block(); + defer (end_error_block()); - Type *bet = base_type(elem_type); - if (!elem_type_can_be_constant(bet)) { - is_constant = false; - } + if (unhandled.count == 1) { + error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); + } else { + error(node, "Unhandled enumerated array cases:"); + for_array(i, unhandled) { + Entity *f = unhandled[i]; + error_line("\t%.*s\n", LIT(f->token.string)); + } + } + error_line("\n"); - if (bet == t_invalid) { - break; + error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); } - bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial"); + } - SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue - map_init(&seen, heap_allocator()); - defer (map_destroy(&seen)); + break; + } - if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { - RangeCache rc = range_cache_make(heap_allocator()); - defer (range_cache_destroy(&rc)); + case Type_Basic: { + if (!is_type_any(t)) { + if (cl->elems.count != 0) { + error(node, "Illegal compound literal"); + } + break; + } + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + { // Checker values + Type *field_types[2] = {t_rawptr, t_typeid}; + isize field_count = 2; + if (cl->elems[0]->kind == Ast_FieldValue) { + bool fields_visited[2] = {}; for_array(i, cl->elems) { Ast *elem = cl->elems[i]; if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); continue; } ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in 'any' literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; - if (is_ast_range(fv->field)) { - Token op = fv->field->BinaryExpr.op; + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + if (sel.entity == nullptr) { + error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name)); + continue; + } - Operand x = {}; - Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type); - if (!ok) { - continue; - } - if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { - error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); - continue; - } + isize index = sel.index[0]; - if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { - error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); - continue; - } + if (fields_visited[index]) { + error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name)); + continue; + } - i64 lo = exact_value_to_i64(x.value); - i64 hi = exact_value_to_i64(y.value); - i64 max_index = hi; - if (op.kind == Token_RangeHalf) { - hi -= 1; - } + fields_visited[index] = true; + check_expr(c, o, fv->value); - bool new_range = range_cache_add_range(&rc, lo, hi); - if (!new_range) { - gbString lo_str = expr_to_string(x.expr); - gbString hi_str = expr_to_string(y.expr); - error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name)); - gb_string_free(hi_str); - gb_string_free(lo_str); - continue; - } + // NOTE(bill): 'any' literals can never be constant + is_constant = false; + check_assignment(c, o, field_types[index], str_lit("'any' literal")); + } + } else { + for_array(index, cl->elems) { + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); + continue; + } - // NOTE(bill): These are sanity checks for invalid enum values - if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) { - gbString lo_str = expr_to_string(x.expr); - error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); - gb_string_free(lo_str); - continue; - } - if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) { - gbString hi_str = expr_to_string(y.expr); - error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); - gb_string_free(hi_str); - continue; - } - if (max < hi) { - max = max_index; - } + check_expr(c, o, elem); + if (index >= field_count) { + error(o->expr, "Too many values in 'any' literal, expected %td", field_count); + break; + } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + // NOTE(bill): 'any' literals can never be constant + is_constant = false; - is_constant = is_constant && operand.mode == Addressing_Constant; + check_assignment(c, o, field_types[index], str_lit("'any' literal")); + } + if (cl->elems.count < field_count) { + error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count); + } + } + } - TokenKind upper_op = Token_LtEq; - if (op.kind == Token_RangeHalf) { - upper_op = Token_Lt; - } - add_to_seen_map(c, &seen, upper_op, x, x, y); - } else { - Operand op_index = {}; - check_expr_with_type_hint(c, &op_index, fv->field, index_type); + break; + } - if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) { - error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); - continue; - } + case Type_Map: { + if (cl->elems.count == 0) { + break; + } + is_constant = false; + { // Checker values + bool key_is_typeid = is_type_typeid(t->Map.key); + bool value_is_typeid = is_type_typeid(t->Map.value); - i64 index = exact_value_to_i64(op_index.value); + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Only 'field = value' elements are allowed in a map literal"); + continue; + } + ast_node(fv, FieldValue, elem); - if (max_type_count >= 0 && (index < total_lo || index > total_hi)) { - gbString idx_str = expr_to_string(op_index.expr); - error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); - gb_string_free(idx_str); - continue; - } + if (key_is_typeid) { + check_expr_or_type(c, o, fv->field, t->Map.key); + } else { + check_expr_with_type_hint(c, o, fv->field, t->Map.key); + } + check_assignment(c, o, t->Map.key, str_lit("map literal")); + if (o->mode == Addressing_Invalid) { + continue; + } - bool new_index = range_cache_add_index(&rc, index); - if (!new_index) { - gbString idx_str = expr_to_string(op_index.expr); - error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name)); - gb_string_free(idx_str); - continue; - } - - if (max < index+1) { - max = index+1; - } + if (value_is_typeid) { + check_expr_or_type(c, o, fv->value, t->Map.value); + } else { + check_expr_with_type_hint(c, o, fv->value, t->Map.value); + } + check_assignment(c, o, t->Map.value, str_lit("map literal")); + } + } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + if (build_context.no_dynamic_literals && cl->elems.count) { + error(node, "Compound literals of dynamic types have been disabled"); + } else { + add_package_dependency(c, "runtime", "__dynamic_map_reserve"); + add_package_dependency(c, "runtime", "__dynamic_map_set"); + } + break; + } - is_constant = is_constant && operand.mode == Addressing_Constant; + case Type_BitSet: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + Type *et = base_type(t->BitSet.elem); + isize field_count = 0; + if (et->kind == Type_Enum) { + field_count = et->Enum.fields.count; + } - add_to_seen_map(c, &seen, op_index); - } + if (cl->elems[0]->kind == Ast_FieldValue) { + error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); + is_constant = false; + } else { + for_array(index, cl->elems) { + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + error(elem, "'field = value' in a bit_set a literal is not allowed"); + continue; } - cl->max_count = max; + check_expr_with_type_hint(c, o, elem, et); - } else { - isize index = 0; - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } - if (e->kind == Ast_FieldValue) { - error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); + if (o->mode == Addressing_Constant) { + i64 lower = t->BitSet.lower; + i64 upper = t->BitSet.upper; + i64 v = exact_value_to_i64(o->value); + if (lower <= v && v <= upper) { + // okay + } else { + error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper); continue; } + } + } + } + break; + } - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); - } + default: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); + gbString str = type_to_string(type); + error(node, "Invalid compound literal type '%s'", str); + gb_string_free(str); + return kind; + } + } - is_constant = is_constant && operand.mode == Addressing_Constant; + if (is_constant) { + o->mode = Addressing_Constant; + + if (is_type_bit_set(type)) { + // NOTE(bill): Encode as an integer + + i64 lower = base_type(type)->BitSet.lower; + + u64 bits = 0; + for_array(index, cl->elems) { + Ast *elem = cl->elems[index]; + GB_ASSERT(elem->kind != Ast_FieldValue); + TypeAndValue tav = elem->tav; + ExactValue i = exact_value_to_integer(tav.value); + if (i.kind != ExactValue_Integer) { + continue; + } + i64 val = big_int_to_i64(&i.value_integer); + val -= lower; + u64 bit = u64(1ll<value = exact_value_u64(bits); + } else if (is_type_constant_type(type) && cl->elems.count == 0) { + ExactValue value = exact_value_compound(node); + Type *bt = core_type(type); + if (bt->kind == Type_Basic) { + if (bt->Basic.flags & BasicFlag_Boolean) { + value = exact_value_bool(false); + } else if (bt->Basic.flags & BasicFlag_Integer) { + value = exact_value_i64(0); + } else if (bt->Basic.flags & BasicFlag_Unsigned) { + value = exact_value_i64(0); + } else if (bt->Basic.flags & BasicFlag_Float) { + value = exact_value_float(0); + } else if (bt->Basic.flags & BasicFlag_Complex) { + value = exact_value_complex(0, 0); + } else if (bt->Basic.flags & BasicFlag_Quaternion) { + value = exact_value_quaternion(0, 0, 0, 0); + } else if (bt->Basic.flags & BasicFlag_Pointer) { + value = exact_value_pointer(0); + } else if (bt->Basic.flags & BasicFlag_String) { + String empty_string = {}; + value = exact_value_string(empty_string); + } else if (bt->Basic.flags & BasicFlag_Rune) { + value = exact_value_i64(0); + } + } + + o->value = value; + } else { + o->value = exact_value_compound(node); + } + } else { + o->mode = Addressing_Value; + } + o->type = type; + return kind; +} + +ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + ast_node(ta, TypeAssertion, node); + check_expr(c, o, ta->expr); + node->viral_state_flags |= ta->expr->viral_state_flags; + + if (o->mode == Addressing_Invalid) { + o->expr = node; + return kind; + } + if (o->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(o->expr); + error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (is_type_untyped(o->type)) { + gbString expr_str = expr_to_string(o->expr); + error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + Type *src = type_deref(o->type); + Type *bsrc = base_type(src); + + + if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) { + if (!is_type_union(src)) { + gbString str = type_to_string(o->type); + error(o->expr, "Type assertions with .? can only operate on unions, got %s", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (bsrc->Union.variants.count != 1 && type_hint != nullptr) { + bool allowed = false; + for_array(i, bsrc->Union.variants) { + Type *vt = bsrc->Union.variants[i]; + if (are_types_identical(vt, type_hint)) { + allowed = true; + add_type_info_type(c, vt); + break; } + } + if (allowed) { + add_type_info_type(c, o->type); + o->type = type_hint; + o->mode = Addressing_OptionalOk; + return kind; + } + } - if (max < index) { - max = index; + if (bsrc->Union.variants.count != 1) { + error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + add_type_info_type(c, o->type); + add_type_info_type(c, bsrc->Union.variants[0]); + + o->type = bsrc->Union.variants[0]; + o->mode = Addressing_OptionalOk; + } else { + Type *t = check_type(c, ta->type); + Type *dst = t; + + if (is_type_union(src)) { + bool ok = false; + for_array(i, bsrc->Union.variants) { + Type *vt = bsrc->Union.variants[i]; + if (are_types_identical(vt, dst)) { + ok = true; + break; } } - bool was_error = false; - if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { - if (0 < max && max < t->EnumeratedArray.count) { - error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max); - was_error = true; + if (!ok) { + gbString expr_str = expr_to_string(o->expr); + gbString dst_type_str = type_to_string(t); + 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(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed"); - was_error = true; + 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; } - // NOTE(bill): Check for missing cases when `#partial literal` is not present - if (cl->elems.count > 0 && !was_error && !is_partial) { - Type *et = base_type(index_type); - GB_ASSERT(et->kind == Type_Enum); - auto fields = et->Enum.fields; + add_type_info_type(c, o->type); + add_type_info_type(c, t); - auto unhandled = array_make(temporary_allocator(), 0, fields.count); + o->type = t; + o->mode = Addressing_OptionalOk; + } else if (is_type_any(src)) { + o->type = t; + o->mode = Addressing_OptionalOk; - for_array(i, fields) { - Entity *f = fields[i]; - if (f->kind != Entity_Constant) { - continue; - } - ExactValue v = f->Constant.value; - auto found = map_get(&seen, hash_exact_value(v)); - if (!found) { - array_add(&unhandled, f); - } - } + add_type_info_type(c, o->type); + add_type_info_type(c, t); + } else { + gbString str = type_to_string(o->type); + error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + } - if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + if ((c->state_flags & StateFlag_no_type_assert) == 0) { + add_package_dependency(c, "runtime", "type_assertion_check"); + add_package_dependency(c, "runtime", "type_assertion_check2"); + } + return kind; +} - if (unhandled.count == 1) { - error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); - } else { - error(node, "Unhandled enumerated array cases:"); - for_array(i, unhandled) { - Entity *f = unhandled[i]; - error_line("\t%.*s\n", LIT(f->token.string)); - } - } - error_line("\n"); +ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(se, SelectorCallExpr, node); + // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables + // COM APIs is a great example of where this kind of thing is extremely useful + // General idea: + // + // x->y(123) == x.y(x, 123) + // + // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes + // Just regenerating a new AST aids things + // + // TODO(bill): Is this a good hack or not? + // + // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I? + + + if (se->modified_call) { + // Prevent double evaluation + o->expr = node; + o->type = node->tav.type; + o->value = node->tav.value; + o->mode = node->tav.mode; + return Expr_Expr; + } - error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); - } - } + bool allow_arrow_right_selector_expr; + allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; + c->allow_arrow_right_selector_expr = true; + Operand x = {}; + ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); + c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - break; + if (x.mode == Addressing_Invalid || x.type == t_invalid) { + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return kind; + } + if (!is_type_proc(x.type)) { + gbString type_str = type_to_string(x.type); + error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); + gb_string_free(type_str); + + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } + + ast_node(ce, CallExpr, se->call); + + GB_ASSERT(x.expr->kind == Ast_SelectorExpr); + + Ast *first_arg = x.expr->SelectorExpr.expr; + GB_ASSERT(first_arg != nullptr); + + Type *pt = base_type(x.type); + GB_ASSERT(pt->kind == Type_Proc); + Type *first_type = nullptr; + String first_arg_name = {}; + if (pt->Proc.param_count > 0) { + Entity *f = pt->Proc.params->Tuple.variables[0]; + first_type = f->type; + first_arg_name = f->token.string; + } + if (first_arg_name.len == 0) { + first_arg_name = str_lit("_"); + } + + if (first_type == nullptr) { + error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } + + Operand y = {}; + y.mode = first_arg->tav.mode; + y.type = first_arg->tav.type; + y.value = first_arg->tav.value; + if (check_is_assignable_to(c, &y, first_type)) { + // Do nothing, it's valid + } else { + Operand z = y; + z.type = type_deref(y.type); + if (check_is_assignable_to(c, &z, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_Pointer}; + first_arg = ast_deref_expr(first_arg->file(), first_arg, op); + } else if (y.mode == Addressing_Variable) { + Operand w = y; + w.type = alloc_type_pointer(y.type); + if (check_is_assignable_to(c, &w, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_And}; + first_arg = ast_unary_expr(first_arg->file(), op, first_arg); + } } + } - case Type_Basic: { - if (!is_type_any(t)) { - if (cl->elems.count != 0) { - error(node, "Illegal compound literal"); - } + if (ce->args.count > 0) { + bool fail = false; + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + fail = true; break; } - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - { // Checker values - Type *field_types[2] = {t_rawptr, t_typeid}; - isize field_count = 2; - if (cl->elems[0]->kind == Ast_FieldValue) { - bool fields_visited[2] = {}; - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in 'any' literal", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; + } + if (!fail && first_is_field_value) { + Token op = {Token_Eq}; + AstFile *f = first_arg->file(); + first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); + } + } - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - if (sel.entity == nullptr) { - error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name)); - continue; - } - isize index = sel.index[0]; - if (fields_visited[index]) { - error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name)); - continue; - } + auto modified_args = slice_make(heap_allocator(), ce->args.count+1); + modified_args[0] = first_arg; + slice_copy(&modified_args, ce->args, 1); + ce->args = modified_args; + se->modified_call = true; + + allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; + c->allow_arrow_right_selector_expr = true; + check_expr_base(c, o, se->call, type_hint); + c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; + + o->expr = node; + return Expr_Expr; +} + + +ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + ast_node(ie, IndexExpr, node); + check_expr(c, o, ie->expr); + node->viral_state_flags |= ie->expr->viral_state_flags; + if (o->mode == Addressing_Invalid) { + o->expr = node; + return kind; + } + + Type *t = base_type(type_deref(o->type)); + bool is_ptr = is_type_pointer(o->type); + bool is_const = o->mode == Addressing_Constant; + + if (is_type_map(t)) { + Operand key = {}; + if (is_type_typeid(t->Map.key)) { + check_expr_or_type(c, &key, ie->index, t->Map.key); + } else { + check_expr_with_type_hint(c, &key, ie->index, t->Map.key); + } + check_assignment(c, &key, t->Map.key, str_lit("map index")); + if (key.mode == Addressing_Invalid) { + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + o->mode = Addressing_MapIndex; + o->type = t->Map.value; + o->expr = node; + + add_package_dependency(c, "runtime", "__dynamic_map_get"); + add_package_dependency(c, "runtime", "__dynamic_map_set"); + return Expr_Expr; + } + + i64 max_count = -1; + bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); + + if (is_const) { + if (is_type_array(t)) { + // OKay + } else if (is_type_slice(t)) { + // Okay + } else if (is_type_enumerated_array(t)) { + // Okay + } else if (is_type_string(t)) { + // Okay + } else if (is_type_relative_slice(t)) { + // Okay + } else if (is_type_matrix(t)) { + // Okay + } else { + valid = false; + } + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + gbString type_str = type_to_string(o->type); + defer (gb_string_free(str)); + defer (gb_string_free(type_str)); + if (is_const) { + error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str); + } else { + error(o->expr, "Cannot index '%s' of type '%s'", str, type_str); + } + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (ie->index == nullptr) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Missing index for '%s'", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + Type *index_type_hint = nullptr; + if (is_type_enumerated_array(t)) { + Type *bt = base_type(t); + GB_ASSERT(bt->kind == Type_EnumeratedArray); + index_type_hint = bt->EnumeratedArray.index; + } + + i64 index = 0; + bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); + if (is_const) { + if (index < 0) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Cannot index a constant '%s'", str); + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } else if (ok) { + ExactValue value = type_and_value_of_expr(ie->expr).value; + o->mode = Addressing_Constant; + bool success = false; + bool finish = false; + o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); + if (!success) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + } + } + + if (type_hint != nullptr && is_type_matrix(t)) { + // TODO(bill): allow matrix columns to be assignable to other types which are the same internally + // if a type hint exists + } + return kind; +} + +ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Stmt; + ast_node(se, SliceExpr, node); + check_expr(c, o, se->expr); + node->viral_state_flags |= se->expr->viral_state_flags; + + if (o->mode == Addressing_Invalid) { + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + bool valid = false; + i64 max_count = -1; + Type *t = base_type(type_deref(o->type)); + switch (t->kind) { + case Type_Basic: + if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { + valid = true; + if (o->mode == Addressing_Constant) { + max_count = o->value.value_string.len; + } + o->type = type_deref(o->type); + } + break; + + case Type_Array: + valid = true; + max_count = t->Array.count; + if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) { + gbString str = expr_to_string(node); + error(node, "Cannot slice array '%s', value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + o->type = alloc_type_slice(t->Array.elem); + break; + + case Type_MultiPointer: + valid = true; + o->type = type_deref(o->type); + break; + + case Type_Slice: + valid = true; + o->type = type_deref(o->type); + break; + + case Type_DynamicArray: + valid = true; + o->type = alloc_type_slice(t->DynamicArray.elem); + break; + + case Type_Struct: + if (is_type_soa_struct(t)) { + valid = true; + o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); + } + break; + + case Type_RelativeSlice: + valid = true; + o->type = t->RelativeSlice.slice_type; + if (o->mode != Addressing_Variable) { + gbString str = expr_to_string(node); + error(node, "Cannot relative slice '%s', value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + break; + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + gbString type_str = type_to_string(o->type); + error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str); + gb_string_free(type_str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (se->low == nullptr && se->high != nullptr) { + // It is okay to continue as it will assume the 1st index is zero + } - fields_visited[index] = true; - check_expr(c, o, fv->value); + i64 indices[2] = {}; + Ast *nodes[2] = {se->low, se->high}; + for (isize i = 0; i < gb_count_of(nodes); i++) { + i64 index = max_count; + if (nodes[i] != nullptr) { + i64 capacity = -1; + if (max_count >= 0) { + capacity = max_count; + } + i64 j = 0; + if (check_index_value(c, t, true, nodes[i], capacity, &j)) { + index = j; + } - // NOTE(bill): 'any' literals can never be constant - is_constant = false; + node->viral_state_flags |= nodes[i]->viral_state_flags; + } else if (i == 0) { + index = 0; + } + indices[i] = index; + } - check_assignment(c, o, field_types[index], str_lit("'any' literal")); - } - } else { - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); - continue; - } + for (isize i = 0; i < gb_count_of(indices); i++) { + i64 a = indices[i]; + for (isize j = i+1; j < gb_count_of(indices); j++) { + i64 b = indices[j]; + if (a > b && b >= 0) { + error(se->close, "Invalid slice indices: [%td > %td]", a, b); + } + } + } + if (max_count < 0) { + if (o->mode == Addressing_Constant) { + gbString s = expr_to_string(se->expr); + error(se->expr, "Cannot slice constant value '%s'", s); + gb_string_free(s); + } + } - check_expr(c, o, elem); - if (index >= field_count) { - error(o->expr, "Too many values in 'any' literal, expected %td", field_count); - break; - } + if (t->kind == Type_MultiPointer && se->high != nullptr) { + /* + x[:] -> [^]T + x[i:] -> [^]T + x[:n] -> []T + x[i:n] -> []T + */ + o->type = alloc_type_slice(t->MultiPointer.elem); + } - // NOTE(bill): 'any' literals can never be constant - is_constant = false; + o->mode = Addressing_Value; - check_assignment(c, o, field_types[index], str_lit("'any' literal")); - } - if (cl->elems.count < field_count) { - error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count); - } + if (is_type_string(t) && max_count >= 0) { + bool all_constant = true; + for (isize i = 0; i < gb_count_of(nodes); i++) { + if (nodes[i] != nullptr) { + TypeAndValue tav = type_and_value_of_expr(nodes[i]); + if (tav.mode != Addressing_Constant) { + all_constant = false; + break; } } - - break; + } + if (!all_constant) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Cannot slice '%s' with non-constant indices", str); + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + gb_string_free(str); + o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring + o->expr = node; + return kind; } - case Type_Map: { - if (cl->elems.count == 0) { - break; - } - is_constant = false; - { // Checker values - bool key_is_typeid = is_type_typeid(t->Map.key); - bool value_is_typeid = is_type_typeid(t->Map.value); + String s = {}; + if (o->value.kind == ExactValue_String) { + s = o->value.value_string; + } - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Only 'field = value' elements are allowed in a map literal"); - continue; - } - ast_node(fv, FieldValue, elem); + o->mode = Addressing_Constant; + o->type = t; + o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + } + return kind; +} - if (key_is_typeid) { - check_expr_or_type(c, o, fv->field, t->Map.key); - } else { - check_expr_with_type_hint(c, o, fv->field, t->Map.key); - } - check_assignment(c, o, t->Map.key, str_lit("map literal")); - if (o->mode == Addressing_Invalid) { - continue; - } +ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + u32 prev_state_flags = c->state_flags; + defer (c->state_flags = prev_state_flags); + if (node->state_flags != 0) { + u32 in = node->state_flags; + u32 out = c->state_flags; - if (value_is_typeid) { - check_expr_or_type(c, o, fv->value, t->Map.value); - } else { - check_expr_with_type_hint(c, o, fv->value, t->Map.value); - } - check_assignment(c, o, t->Map.value, str_lit("map literal")); - } - } + if (in & StateFlag_no_bounds_check) { + out |= StateFlag_no_bounds_check; + out &= ~StateFlag_bounds_check; + } else if (in & StateFlag_bounds_check) { + out |= StateFlag_bounds_check; + out &= ~StateFlag_no_bounds_check; + } - if (build_context.no_dynamic_literals && cl->elems.count) { - error(node, "Compound literals of dynamic types have been disabled"); - } else { - add_package_dependency(c, "runtime", "__dynamic_map_reserve"); - add_package_dependency(c, "runtime", "__dynamic_map_set"); - } - break; + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; } - case Type_BitSet: { - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - Type *et = base_type(t->BitSet.elem); - isize field_count = 0; - if (et->kind == Type_Enum) { - field_count = et->Enum.fields.count; - } + c->state_flags = out; + } - if (cl->elems[0]->kind == Ast_FieldValue) { - error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); - is_constant = false; - } else { - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - error(elem, "'field = value' in a bit_set a literal is not allowed"); - continue; - } + ExprKind kind = Expr_Stmt; - check_expr_with_type_hint(c, o, elem, et); + o->mode = Addressing_Invalid; + o->type = t_invalid; - if (is_constant) { - is_constant = o->mode == Addressing_Constant; - } + switch (node->kind) { + default: + return kind; - check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); - if (o->mode == Addressing_Constant) { - i64 lower = t->BitSet.lower; - i64 upper = t->BitSet.upper; - i64 v = exact_value_to_i64(o->value); - if (lower <= v && v <= upper) { - // okay - } else { - error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper); - continue; - } - } + case_ast_node(be, BadExpr, node) + return kind; + case_end; + + case_ast_node(i, Implicit, node) + switch (i->kind) { + case Token_context: + { + if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) { + error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl); + return kind; + } + if (unparen_expr(c->assignment_lhs_hint) == node) { + c->scope->flags |= ScopeFlag_ContextDefined; } - } - break; - } - default: { - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init + if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { + error(node, "'context' has not been defined within this scope"); + // Continue with value + } + + init_core_context(c->checker); + o->mode = Addressing_Context; + o->type = t_context; } + break; - gbString str = type_to_string(type); - error(node, "Invalid compound literal type '%s'", str); - gb_string_free(str); + default: + error(node, "Illegal implicit name '%.*s'", LIT(i->string)); return kind; } - } - - if (is_constant) { - o->mode = Addressing_Constant; + case_end; - if (is_type_bit_set(type)) { - // NOTE(bill): Encode as an integer + case_ast_node(i, Ident, node); + check_ident(c, o, node, nullptr, type_hint, false); + case_end; - i64 lower = base_type(type)->BitSet.lower; + case_ast_node(u, Undef, node); + o->mode = Addressing_Value; + o->type = t_untyped_undef; + case_end; - u64 bits = 0; - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - GB_ASSERT(elem->kind != Ast_FieldValue); - TypeAndValue tav = elem->tav; - ExactValue i = exact_value_to_integer(tav.value); - if (i.kind != ExactValue_Integer) { - continue; - } - i64 val = big_int_to_i64(&i.value_integer); - val -= lower; - u64 bit = u64(1ll<value = exact_value_u64(bits); - } else if (is_type_constant_type(type) && cl->elems.count == 0) { - ExactValue value = exact_value_compound(node); - Type *bt = core_type(type); - if (bt->kind == Type_Basic) { - if (bt->Basic.flags & BasicFlag_Boolean) { - value = exact_value_bool(false); - } else if (bt->Basic.flags & BasicFlag_Integer) { - value = exact_value_i64(0); - } else if (bt->Basic.flags & BasicFlag_Unsigned) { - value = exact_value_i64(0); - } else if (bt->Basic.flags & BasicFlag_Float) { - value = exact_value_float(0); - } else if (bt->Basic.flags & BasicFlag_Complex) { - value = exact_value_complex(0, 0); - } else if (bt->Basic.flags & BasicFlag_Quaternion) { - value = exact_value_quaternion(0, 0, 0, 0); - } else if (bt->Basic.flags & BasicFlag_Pointer) { - value = exact_value_pointer(0); - } else if (bt->Basic.flags & BasicFlag_String) { - String empty_string = {}; - value = exact_value_string(empty_string); - } else if (bt->Basic.flags & BasicFlag_Rune) { - value = exact_value_i64(0); - } - } - o->value = value; - } else { - o->value = exact_value_compound(node); + case_ast_node(bl, BasicLit, node); + Type *t = t_invalid; + switch (node->tav.value.kind) { + case ExactValue_String: t = t_untyped_string; break; + case ExactValue_Float: t = t_untyped_float; break; + case ExactValue_Complex: t = t_untyped_complex; break; + case ExactValue_Quaternion: t = t_untyped_quaternion; break; + case ExactValue_Integer: + t = t_untyped_integer; + if (bl->token.kind == Token_Rune) { + t = t_untyped_rune; } - } else { - o->mode = Addressing_Value; + break; + default: + GB_PANIC("Unhandled value type for basic literal"); + break; } - o->type = type; - case_end; - case_ast_node(pe, ParenExpr, node); - kind = check_expr_base(c, o, pe->expr, type_hint); - node->viral_state_flags |= pe->expr->viral_state_flags; - o->expr = node; + o->mode = Addressing_Constant; + o->type = t; + o->value = node->tav.value; case_end; - case_ast_node(te, TagExpr, node); - String name = te->name.string; - error(node, "Unknown tag expression, #%.*s", LIT(name)); - if (te->expr) { - kind = check_expr_base(c, o, te->expr, type_hint); - node->viral_state_flags |= te->expr->viral_state_flags; - } - o->expr = node; + case_ast_node(bd, BasicDirective, node); + kind = check_basic_directive_expr(c, o, node, type_hint); case_end; - case_ast_node(ta, TypeAssertion, node); - check_expr(c, o, ta->expr); - node->viral_state_flags |= ta->expr->viral_state_flags; - - if (o->mode == Addressing_Invalid) { - o->expr = node; - return kind; - } - if (o->mode == Addressing_Constant) { - gbString expr_str = expr_to_string(o->expr); - error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str); - gb_string_free(expr_str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } + case_ast_node(pg, ProcGroup, node); + error(node, "Illegal use of a procedure group"); + o->mode = Addressing_Invalid; + case_end; - if (is_type_untyped(o->type)) { - gbString expr_str = expr_to_string(o->expr); - error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str); - gb_string_free(expr_str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } + case_ast_node(pl, ProcLit, node); + CheckerContext ctx = *c; - Type *src = type_deref(o->type); - Type *bsrc = base_type(src); + DeclInfo *decl = nullptr; + Type *type = alloc_type(Type_Proc); + check_open_scope(&ctx, pl->type); + { + decl = make_decl_info(ctx.scope, ctx.decl); + decl->proc_lit = node; + ctx.decl = decl; + defer (ctx.decl = ctx.decl->parent); + if (pl->tags != 0) { + error(node, "A procedure literal cannot have tags"); + pl->tags = 0; // TODO(bill): Should I zero this?! + } - if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) { - if (!is_type_union(src)) { - gbString str = type_to_string(o->type); - error(o->expr, "Type assertions with .? can only operate on unions, got %s", str); + check_procedure_type(&ctx, type, pl->type); + if (!is_type_proc(type)) { + gbString str = expr_to_string(node); + error(node, "Invalid procedure literal '%s'", str); gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; + check_close_scope(&ctx); return kind; } - if (bsrc->Union.variants.count != 1 && type_hint != nullptr) { - bool allowed = false; - for_array(i, bsrc->Union.variants) { - Type *vt = bsrc->Union.variants[i]; - if (are_types_identical(vt, type_hint)) { - allowed = true; - add_type_info_type(c, vt); - break; - } - } - if (allowed) { - add_type_info_type(c, o->type); - o->type = type_hint; - o->mode = Addressing_OptionalOk; - return kind; - } - } - - if (bsrc->Union.variants.count != 1) { - error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count); - o->mode = Addressing_Invalid; - o->expr = node; + if (pl->body == nullptr) { + error(node, "A procedure literal must have a body"); return kind; } - add_type_info_type(c, o->type); - add_type_info_type(c, bsrc->Union.variants[0]); + pl->decl = decl; + check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags); + } + check_close_scope(&ctx); - o->type = bsrc->Union.variants[0]; - o->mode = Addressing_OptionalOk; - } else { - Type *t = check_type(c, ta->type); - Type *dst = t; + o->mode = Addressing_Value; + o->type = type; + case_end; - if (is_type_union(src)) { - bool ok = false; - for_array(i, bsrc->Union.variants) { - Type *vt = bsrc->Union.variants[i]; - if (are_types_identical(vt, dst)) { - ok = true; - break; - } - } + case_ast_node(te, TernaryIfExpr, node); + kind = check_ternary_if_expr(c, o, node, type_hint); + case_end; + + case_ast_node(te, TernaryWhenExpr, node); + kind = check_ternary_when_expr(c, o, node, type_hint); + case_end; - if (!ok) { - gbString expr_str = expr_to_string(o->expr); - gbString dst_type_str = type_to_string(t); - 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; - } + case_ast_node(oe, OrElseExpr, node); + return check_or_else_expr(c, o, node, type_hint); + case_end; - add_type_info_type(c, o->type); - add_type_info_type(c, t); + case_ast_node(re, OrReturnExpr, node); + return check_or_return_expr(c, o, node, type_hint); + case_end; - o->type = t; - o->mode = Addressing_OptionalOk; - } else if (is_type_any(src)) { - o->type = t; - o->mode = Addressing_OptionalOk; + case_ast_node(cl, CompoundLit, node); + kind = check_compound_literal(c, o, node, type_hint); + case_end; - add_type_info_type(c, o->type); - add_type_info_type(c, t); - } else { - gbString str = type_to_string(o->type); - error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } + case_ast_node(pe, ParenExpr, node); + kind = check_expr_base(c, o, pe->expr, type_hint); + node->viral_state_flags |= pe->expr->viral_state_flags; + o->expr = node; + case_end; + + case_ast_node(te, TagExpr, node); + String name = te->name.string; + error(node, "Unknown tag expression, #%.*s", LIT(name)); + if (te->expr) { + kind = check_expr_base(c, o, te->expr, type_hint); + node->viral_state_flags |= te->expr->viral_state_flags; } + o->expr = node; + case_end; - add_package_dependency(c, "runtime", "type_assertion_check"); - add_package_dependency(c, "runtime", "type_assertion_check2"); + case_ast_node(ta, TypeAssertion, node); + kind = check_type_assertion(c, o, node, type_hint); case_end; case_ast_node(tc, TypeCast, node); @@ -8669,443 +9161,19 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(se, SelectorCallExpr, node); - // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables - // COM APIs is a great example of where this kind of thing is extremely useful - // General idea: - // - // x->y(123) == x.y(x, 123) - // - // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes - // Just regenerating a new AST aids things - // - // TODO(bill): Is this a good hack or not? - // - // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I? - - - if (se->modified_call) { - // Prevent double evaluation - o->expr = node; - o->type = node->tav.type; - o->value = node->tav.value; - o->mode = node->tav.mode; - return Expr_Expr; - } - - bool allow_arrow_right_selector_expr; - allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; - c->allow_arrow_right_selector_expr = true; - Operand x = {}; - ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); - c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - - if (x.mode == Addressing_Invalid || x.type == t_invalid) { - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return kind; - } - if (!is_type_proc(x.type)) { - gbString type_str = type_to_string(x.type); - error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); - gb_string_free(type_str); - - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return Expr_Stmt; - } - - ast_node(ce, CallExpr, se->call); - - GB_ASSERT(x.expr->kind == Ast_SelectorExpr); - - Ast *first_arg = x.expr->SelectorExpr.expr; - GB_ASSERT(first_arg != nullptr); - - Type *pt = base_type(x.type); - GB_ASSERT(pt->kind == Type_Proc); - Type *first_type = nullptr; - String first_arg_name = {}; - if (pt->Proc.param_count > 0) { - Entity *f = pt->Proc.params->Tuple.variables[0]; - first_type = f->type; - first_arg_name = f->token.string; - } - if (first_arg_name.len == 0) { - first_arg_name = str_lit("_"); - } - - if (first_type == nullptr) { - error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return Expr_Stmt; - } - - Operand y = {}; - y.mode = first_arg->tav.mode; - y.type = first_arg->tav.type; - y.value = first_arg->tav.value; - if (check_is_assignable_to(c, &y, first_type)) { - // Do nothing, it's valid - } else { - Operand z = y; - z.type = type_deref(y.type); - if (check_is_assignable_to(c, &z, first_type)) { - // NOTE(bill): AST GENERATION HACK! - Token op = {Token_Pointer}; - first_arg = ast_deref_expr(first_arg->file(), first_arg, op); - } else if (y.mode == Addressing_Variable) { - Operand w = y; - w.type = alloc_type_pointer(y.type); - if (check_is_assignable_to(c, &w, first_type)) { - // NOTE(bill): AST GENERATION HACK! - Token op = {Token_And}; - first_arg = ast_unary_expr(first_arg->file(), op, first_arg); - } - } - } - - if (ce->args.count > 0) { - bool fail = false; - bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; - } - if (mix) { - fail = true; - break; - } - } - if (!fail && first_is_field_value) { - Token op = {Token_Eq}; - AstFile *f = first_arg->file(); - first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); - } - } - - - - auto modified_args = slice_make(heap_allocator(), ce->args.count+1); - modified_args[0] = first_arg; - slice_copy(&modified_args, ce->args, 1); - ce->args = modified_args; - se->modified_call = true; - - allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; - c->allow_arrow_right_selector_expr = true; - check_expr_base(c, o, se->call, type_hint); - c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - - o->expr = node; - return Expr_Expr; + return check_selector_call_expr(c, o, node, type_hint); case_end; - case_ast_node(ise, ImplicitSelectorExpr, node); return check_implicit_selector_expr(c, o, node, type_hint); case_end; case_ast_node(ie, IndexExpr, node); - check_expr(c, o, ie->expr); - node->viral_state_flags |= ie->expr->viral_state_flags; - if (o->mode == Addressing_Invalid) { - o->expr = node; - return kind; - } - - Type *t = base_type(type_deref(o->type)); - bool is_ptr = is_type_pointer(o->type); - bool is_const = o->mode == Addressing_Constant; - - if (is_type_map(t)) { - Operand key = {}; - if (is_type_typeid(t->Map.key)) { - check_expr_or_type(c, &key, ie->index, t->Map.key); - } else { - check_expr_with_type_hint(c, &key, ie->index, t->Map.key); - } - check_assignment(c, &key, t->Map.key, str_lit("map index")); - if (key.mode == Addressing_Invalid) { - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - o->mode = Addressing_MapIndex; - o->type = t->Map.value; - o->expr = node; - - add_package_dependency(c, "runtime", "__dynamic_map_get"); - add_package_dependency(c, "runtime", "__dynamic_map_set"); - return Expr_Expr; - } - - i64 max_count = -1; - bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); - - if (is_const) { - if (is_type_array(t)) { - // OKay - } else if (is_type_slice(t)) { - // Okay - } else if (is_type_enumerated_array(t)) { - // Okay - } else if (is_type_string(t)) { - // Okay - } else if (is_type_relative_slice(t)) { - // Okay - } else if (is_type_matrix(t)) { - // Okay - } else { - valid = false; - } - } - - if (!valid) { - gbString str = expr_to_string(o->expr); - gbString type_str = type_to_string(o->type); - defer (gb_string_free(str)); - defer (gb_string_free(type_str)); - if (is_const) { - error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str); - } else { - error(o->expr, "Cannot index '%s' of type '%s'", str, type_str); - } - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - if (ie->index == nullptr) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Missing index for '%s'", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - Type *index_type_hint = nullptr; - if (is_type_enumerated_array(t)) { - Type *bt = base_type(t); - GB_ASSERT(bt->kind == Type_EnumeratedArray); - index_type_hint = bt->EnumeratedArray.index; - } - - i64 index = 0; - bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); - if (is_const) { - if (index < 0) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Cannot index a constant '%s'", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } else if (ok) { - ExactValue value = type_and_value_of_expr(ie->expr).value; - o->mode = Addressing_Constant; - bool success = false; - bool finish = false; - o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); - if (!success) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - } - } - - if (type_hint != nullptr && is_type_matrix(t)) { - // TODO(bill): allow matrix columns to be assignable to other types which are the same internally - // if a type hint exists - } - + kind = check_index_expr(c, o, node, type_hint); case_end; case_ast_node(se, SliceExpr, node); - check_expr(c, o, se->expr); - node->viral_state_flags |= se->expr->viral_state_flags; - - if (o->mode == Addressing_Invalid) { - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - bool valid = false; - i64 max_count = -1; - Type *t = base_type(type_deref(o->type)); - switch (t->kind) { - case Type_Basic: - if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { - valid = true; - if (o->mode == Addressing_Constant) { - max_count = o->value.value_string.len; - } - o->type = type_deref(o->type); - } - break; - - case Type_Array: - valid = true; - max_count = t->Array.count; - if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) { - gbString str = expr_to_string(node); - error(node, "Cannot slice array '%s', value is not addressable", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - o->type = alloc_type_slice(t->Array.elem); - break; - - case Type_MultiPointer: - valid = true; - o->type = type_deref(o->type); - break; - - case Type_Slice: - valid = true; - o->type = type_deref(o->type); - break; - - case Type_DynamicArray: - valid = true; - o->type = alloc_type_slice(t->DynamicArray.elem); - break; - - case Type_Struct: - if (is_type_soa_struct(t)) { - valid = true; - o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); - } - break; - - case Type_RelativeSlice: - valid = true; - o->type = t->RelativeSlice.slice_type; - if (o->mode != Addressing_Variable) { - gbString str = expr_to_string(node); - error(node, "Cannot relative slice '%s', value is not addressable", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - break; - } - - if (!valid) { - gbString str = expr_to_string(o->expr); - gbString type_str = type_to_string(o->type); - error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str); - gb_string_free(type_str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - if (se->low == nullptr && se->high != nullptr) { - // It is okay to continue as it will assume the 1st index is zero - } - - i64 indices[2] = {}; - Ast *nodes[2] = {se->low, se->high}; - for (isize i = 0; i < gb_count_of(nodes); i++) { - i64 index = max_count; - if (nodes[i] != nullptr) { - i64 capacity = -1; - if (max_count >= 0) { - capacity = max_count; - } - i64 j = 0; - if (check_index_value(c, t, true, nodes[i], capacity, &j)) { - index = j; - } - - node->viral_state_flags |= nodes[i]->viral_state_flags; - } else if (i == 0) { - index = 0; - } - indices[i] = index; - } - - for (isize i = 0; i < gb_count_of(indices); i++) { - i64 a = indices[i]; - for (isize j = i+1; j < gb_count_of(indices); j++) { - i64 b = indices[j]; - if (a > b && b >= 0) { - error(se->close, "Invalid slice indices: [%td > %td]", a, b); - } - } - } - - if (max_count < 0) { - if (o->mode == Addressing_Constant) { - gbString s = expr_to_string(se->expr); - error(se->expr, "Cannot slice constant value '%s'", s); - gb_string_free(s); - } - } - - if (t->kind == Type_MultiPointer && se->high != nullptr) { - /* - x[:] -> [^]T - x[i:] -> [^]T - x[:n] -> []T - x[i:n] -> []T - */ - o->type = alloc_type_slice(t->MultiPointer.elem); - } - - o->mode = Addressing_Value; - - if (is_type_string(t) && max_count >= 0) { - bool all_constant = true; - for (isize i = 0; i < gb_count_of(nodes); i++) { - if (nodes[i] != nullptr) { - TypeAndValue tav = type_and_value_of_expr(nodes[i]); - if (tav.mode != Addressing_Constant) { - all_constant = false; - break; - } - } - } - if (!all_constant) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Cannot slice '%s' with non-constant indices", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); - gb_string_free(str); - o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring - o->expr = node; - return kind; - } - - String s = {}; - if (o->value.kind == ExactValue_String) { - s = o->value.value_string; - } - - o->mode = Addressing_Constant; - o->type = t; - o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); - } - + kind = check_slice_expr(c, o, node, type_hint); case_end; case_ast_node(mie, MatrixIndexExpr, node); @@ -9230,6 +9298,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return kind; } + + ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = check_expr_base_internal(c, o, node, type_hint); if (o->type != nullptr && core_type(o->type) == nullptr) { -- cgit v1.2.3 From 445ca705210999e106b6aeb265cfb2979cbd857c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 16:11:48 +0000 Subject: Correct implicit union cast --- src/check_expr.cpp | 11 +++++++++++ src/llvm_backend_expr.cpp | 9 +++++++++ 2 files changed, 20 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fb58839bc..3f31ac810 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -508,6 +508,10 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type); #define MAXIMUM_TYPE_DISTANCE 10 i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) { + if (c == nullptr) { + GB_ASSERT(operand->mode == Addressing_Value); + GB_ASSERT(is_type_typed(operand->type)); + } if (operand->mode == Addressing_Invalid || type == t_invalid) { return -1; @@ -818,6 +822,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { return check_is_assignable_to_with_score(c, operand, type, &score); } +bool internal_check_is_assignable_to(Type *src, Type *dst) { + Operand x = {}; + x.type = src; + x.mode = Addressing_Value; + return check_is_assignable_to(nullptr, &x, dst); +} + AstPackage *get_package_of_type(Type *type) { for (;;) { if (type == nullptr) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index ea031ee56..715b7df78 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1834,6 +1834,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return lb_addr_load(p, parent); } } + if (dst->Union.variants.count == 1) { + Type *vt = dst->Union.variants[0]; + if (internal_check_is_assignable_to(src, vt)) { + value = lb_emit_conv(p, value, vt); + lbAddr parent = lb_add_local_generated(p, t, true); + lb_emit_store_union_variant(p, parent.addr, value, vt); + return lb_addr_load(p, parent); + } + } } // NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's -- cgit v1.2.3 From c5d348515dcddbd2c29aca79f1863dd36af5476a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 8 Feb 2022 22:59:37 +0000 Subject: Add `intrinsics.type_is_subtype_of`; `intrinsics.objc_selector_name` --- src/check_builtin.cpp | 42 ++++++++++++++++++++++++++++++++++++----- src/check_expr.cpp | 36 ----------------------------------- src/checker_builtin_procs.hpp | 8 ++++++-- src/llvm_backend_proc.cpp | 3 +++ src/llvm_backend_utility.cpp | 11 +++++++++++ src/types.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 43 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 8ada77b12..df22d82e2 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -350,9 +350,15 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call return true; } break; - case BuiltinProc_objc_create: { - GB_PANIC("TODO: BuiltinProc_objc_create"); - return false; + case BuiltinProc_objc_selector_name: { + String sel_name = {}; + if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) { + return false; + } + + operand->type = t_objc_SEL; + operand->mode = Addressing_Value; + return true; } break; } } @@ -392,8 +398,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_len: case BuiltinProc_min: case BuiltinProc_max: + case BuiltinProc_type_is_subtype_of: case BuiltinProc_objc_send: - case BuiltinProc_objc_create: + case BuiltinProc_objc_selector_name: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -435,7 +442,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_objc_send: - case BuiltinProc_objc_create: + case BuiltinProc_objc_selector_name: return check_builtin_objc_procedure(c, operand, call, id, type_hint); case BuiltinProc___entry_point: @@ -3945,6 +3952,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; + case BuiltinProc_type_is_subtype_of: + { + Operand op_src = {}; + Operand op_dst = {}; + + check_expr_or_type(c, &op_src, ce->args[0]); + if (op_src.mode != Addressing_Type) { + gbString e = expr_to_string(op_src.expr); + error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + check_expr_or_type(c, &op_dst, ce->args[1]); + if (op_dst.mode != Addressing_Type) { + gbString e = expr_to_string(op_dst.expr); + error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + + operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type)); + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + } break; + case BuiltinProc_type_field_index_of: { Operand op = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3f31ac810..6db17a316 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -228,42 +228,6 @@ void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reser } } - -isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { - Type *prev_src = src; - src = type_deref(src); - if (!src_is_ptr) { - src_is_ptr = src != prev_src; - } - src = base_type(src); - - if (!is_type_struct(src)) { - return 0; - } - - for_array(i, src->Struct.fields) { - Entity *f = src->Struct.fields[i]; - if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { - continue; - } - - if (are_types_identical(f->type, dst)) { - return level+1; - } - if (src_is_ptr && is_type_pointer(dst)) { - if (are_types_identical(f->type, type_deref(dst))) { - return level+1; - } - } - isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); - if (nested_level > 0) { - return nested_level; - } - } - - return 0; -} - bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type, Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 1a9d7d7a0..c14c18412 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -241,6 +241,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_polymorphic_record_parameter_count, BuiltinProc_type_polymorphic_record_parameter_value, + BuiltinProc_type_is_subtype_of, + BuiltinProc_type_field_index_of, BuiltinProc_type_equal_proc, @@ -251,7 +253,7 @@ BuiltinProc__type_end, BuiltinProc___entry_point, BuiltinProc_objc_send, - BuiltinProc_objc_create, + BuiltinProc_objc_selector_name, BuiltinProc_COUNT, }; @@ -494,6 +496,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -505,5 +509,5 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("objc_create"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_selector_name"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 6de0aaed7..caa3dfa1a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2109,6 +2109,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_objc_send: return lb_handle_obj_send(p, expr); + + case BuiltinProc_objc_selector_name: + return lb_handle_obj_selector_name(p, expr); } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index d92f711ba..8ef66df7a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1915,3 +1915,14 @@ lbValue lb_handle_obj_send(lbProcedure *p, Ast *expr) { return lb_emit_call(p, the_proc, args); } + + +lbValue lb_handle_obj_selector_name(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + return lb_handle_obj_selector(p, name); + +} \ No newline at end of file diff --git a/src/types.cpp b/src/types.cpp index cdb31c6e1..024d644a2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3748,6 +3748,50 @@ i64 type_offset_of_from_selection(Type *type, Selection sel) { return offset; } +isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { + Type *prev_src = src; + src = type_deref(src); + if (!src_is_ptr) { + src_is_ptr = src != prev_src; + } + src = base_type(src); + + if (!is_type_struct(src)) { + return 0; + } + + for_array(i, src->Struct.fields) { + Entity *f = src->Struct.fields[i]; + if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { + continue; + } + + if (are_types_identical(f->type, dst)) { + return level+1; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(f->type, type_deref(dst))) { + return level+1; + } + } + isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); + if (nested_level > 0) { + return nested_level; + } + } + + return 0; +} + +bool is_type_subtype_of(Type *src, Type *dst) { + if (are_types_identical(src, dst)) { + return true; + } + + return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src)); +} + + Type *get_struct_field_type(Type *t, isize index) { t = base_type(type_deref(t)); -- cgit v1.2.3 From 0e5928ff39653d3a1de3e071afbcc1b29f5fe9f7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Feb 2022 15:16:30 +0000 Subject: Correct pseudo selector code generation --- src/check_expr.cpp | 6 +++--- src/llvm_backend_expr.cpp | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6db17a316..7fb0e44f2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6396,10 +6396,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr return builtin_procs[id].kind; } - Entity *e = entity_of_node(operand->expr); + Entity *initial_entity = entity_of_node(operand->expr); - if (e != nullptr && e->kind == Entity_Procedure) { - if (e->Procedure.deferred_procedure.entity != nullptr) { + if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) { + if (initial_entity->Procedure.deferred_procedure.entity != nullptr) { call->viral_state_flags |= ViralStateFlag_ContainsDeferredProcedure; } } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index b2f430cd2..844deb43c 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3303,9 +3303,9 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SelectorExpr, expr); - Ast *sel = unparen_expr(se->selector); - if (sel->kind == Ast_Ident) { - String selector = sel->Ident.token.string; + Ast *sel_node = unparen_expr(se->selector); + if (sel_node->kind == Ast_Ident) { + String selector = sel_node->Ident.token.string; TypeAndValue tav = type_and_value_of_expr(se->expr); if (tav.mode == Addressing_Invalid) { @@ -3354,7 +3354,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { GB_ASSERT(sel.entity != nullptr); if (sel.pseudo_field) { GB_ASSERT(sel.entity->kind == Entity_Procedure); - return lb_addr(lb_find_value_from_entity(p->module, sel.entity)); + Entity *e = entity_of_node(sel_node); + return lb_addr(lb_find_value_from_entity(p->module, e)); } { -- cgit v1.2.3 From 65dedbb1caaa785a444d32a7a15adaf6c396b07f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 11:54:15 +0000 Subject: Add `#subtype` struct field prefix, required to have a COM interface hierarchy --- src/check_expr.cpp | 3 +++ src/check_type.cpp | 18 ++++++++++++++++++ src/entity.cpp | 5 +++++ src/parser.cpp | 4 +++- src/parser.hpp | 3 ++- src/types.cpp | 12 ++++++------ 6 files changed, 37 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7fb0e44f2..884f1bb9f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9774,6 +9774,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { if (f->flags&FieldFlag_const) { str = gb_string_appendc(str, "#const "); } + if (f->flags&FieldFlag_subtype) { + str = gb_string_appendc(str, "#subtype "); + } for_array(i, f->names) { Ast *name = f->names[i]; diff --git a/src/check_type.cpp b/src/check_type.cpp index 32340070e..7e0ad2bd9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields } bool is_using = (p->flags&FieldFlag_using) != 0; + bool is_subtype = (p->flags&FieldFlag_subtype) != 0; for_array(j, p->names) { Ast *name = p->names[j]; @@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx, ctx->scope, name, field); field->Variable.field_group_index = field_group_index; + if (is_subtype) { + field->flags |= EntityFlag_Subtype; + } if (j == 0) { field->Variable.docs = docs; @@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields populate_using_entity_scope(ctx, node, p, type); } + + if (is_subtype && p->names.count > 0) { + Type *first_type = fields_array[fields_array.count-1]->type; + Type *t = base_type(type_deref(first_type)); + + if (!does_field_type_allow_using(t) && + p->names.count >= 1 && + p->names[0]->kind == Ast_Ident) { + Token name_token = p->names[0]->Ident.token; + gbString type_str = type_to_string(first_type); + error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str); + gb_string_free(type_str); + } + } } *fields = slice_from_array(fields_array); diff --git a/src/entity.cpp b/src/entity.cpp index df8ee3faa..f5720293f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -74,6 +74,7 @@ enum EntityFlag : u64 { EntityFlag_Test = 1ull<<30, EntityFlag_Init = 1ull<<31, + EntityFlag_Subtype = 1ull<<32, EntityFlag_CustomLinkName = 1ull<<40, EntityFlag_CustomLinkage_Internal = 1ull<<41, @@ -86,6 +87,10 @@ enum EntityFlag : u64 { EntityFlag_Overridden = 1ull<<63, }; +enum : u64 { + EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype, +}; + enum EntityState : u32 { EntityState_Unresolved = 0, EntityState_InProgress = 1, diff --git a/src/parser.cpp b/src/parser.cpp index 0914c77ca..b55d745f1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3504,12 +3504,13 @@ enum FieldPrefixKind : i32 { FieldPrefix_Unknown = -1, FieldPrefix_Invalid = 0, - FieldPrefix_using, + FieldPrefix_using, // implies #subtype FieldPrefix_const, FieldPrefix_no_alias, FieldPrefix_c_vararg, FieldPrefix_auto_cast, FieldPrefix_any_int, + FieldPrefix_subtype, // does not imply `using` semantics }; struct ParseFieldPrefixMapping { @@ -3526,6 +3527,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { {str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg}, {str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int}, + {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype}, }; diff --git a/src/parser.hpp b/src/parser.hpp index ff0df0382..fb84210b3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,6 +282,7 @@ enum FieldFlag : u32 { FieldFlag_auto_cast = 1<<4, FieldFlag_const = 1<<5, FieldFlag_any_int = 1<<6, + FieldFlag_subtype = 1<<7, // Internal use by the parser only FieldFlag_Tags = 1<<10, @@ -289,7 +290,7 @@ enum FieldFlag : u32 { // Parameter List Restrictions FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int, - FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags, + FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; enum StmtAllowFlag { diff --git a/src/types.cpp b/src/types.cpp index 78958146b..2c1e6162f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2334,7 +2334,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { GB_ASSERT(is_type_struct(src) || is_type_union(src)); for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) { + if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) { if (are_types_identical(dst, f->type)) { return f->token.string; } @@ -2343,7 +2343,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { return f->token.string; } } - if (is_type_struct(f->type)) { + if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) { String name = lookup_subtype_polymorphic_field(dst, f->type); if (name.len > 0) { return name; @@ -2489,9 +2489,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (xf->token.string != yf->token.string) { return false; } - bool xf_is_using = (xf->flags&EntityFlag_Using) != 0; - bool yf_is_using = (yf->flags&EntityFlag_Using) != 0; - if (xf_is_using ^ yf_is_using) { + u64 xf_flags = (xf->flags&EntityFlags_IsSubtype); + u64 yf_flags = (yf->flags&EntityFlags_IsSubtype); + if (xf_flags != yf_flags) { return false; } } @@ -3813,7 +3813,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0 for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { + if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { continue; } -- cgit v1.2.3 From e81ed9a9601f6a7761dec5b0f96cc8680a16f166 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 23:19:49 +0000 Subject: Add "Did you mean" to Objective-C fields --- src/check_expr.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 884f1bb9f..e07dc3d60 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") { } } +void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) { + if (e->kind != Entity_TypeName) { + return; + } + if (e->TypeName.objc_metadata == nullptr) { + return; + } + TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata; + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Struct); + + if (is_type) { + for_array(i, objc_metadata->type_entries) { + String name = objc_metadata->type_entries[i].name; + string_set_add(set, name); + } + } else { + for_array(i, objc_metadata->value_entries) { + String name = objc_metadata->value_entries[i].name; + string_set_add(set, name); + } + } + + for_array(i, t->Struct.fields) { + Entity *f = t->Struct.fields[i]; + if (f->flags & EntityFlag_Using && f->type != nullptr) { + if (f->type->kind == Type_Named && f->type->Named.type_name) { + populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type); + } + } + } +} + + +void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") { + ERROR_BLOCK(); + GB_ASSERT(e->kind == Entity_TypeName); + GB_ASSERT(e->TypeName.objc_metadata != nullptr); + auto *objc_metadata = e->TypeName.objc_metadata; + mutex_lock(objc_metadata->mutex); + defer (mutex_unlock(objc_metadata->mutex)); + + StringSet set = {}; + string_set_init(&set, heap_allocator()); + defer (string_set_destroy(&set)); + populate_check_did_you_mean_objc_entity(&set, e, is_type); + + + DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name); + defer (did_you_mean_destroy(&d)); + for_array(i, set.entries) { + did_you_mean_append(&d, set.entries[i].value); + } + check_did_you_mean_print(&d, prefix); +} + void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") { ERROR_BLOCK(); @@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array const &fields, check_did_you_mean_print(&d, prefix); } + void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") { ERROR_BLOCK(); @@ -4420,7 +4477,12 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (operand->type != nullptr && selector->kind == Ast_Ident) { String const &name = selector->Ident.token.string; Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { + if (operand->type->kind == Type_Named && + operand->type->Named.type_name && + operand->type->Named.type_name->kind == Entity_TypeName && + operand->type->Named.type_name->TypeName.objc_metadata) { + check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type); + } else if (bt->kind == Type_Struct) { check_did_you_mean_type(name, bt->Struct.fields); } else if (bt->kind == Type_Enum) { check_did_you_mean_type(name, bt->Enum.fields); -- cgit v1.2.3 From 278e239973ab1e680bd36f90c069ec798930e54b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 28 Feb 2022 13:39:27 +0000 Subject: Commit rest of code for `-disallow-rtti` --- src/build_settings.cpp | 3 ++- src/check_builtin.cpp | 18 +++++++++++++----- src/check_decl.cpp | 2 ++ src/check_expr.cpp | 2 ++ src/check_type.cpp | 2 ++ src/checker.cpp | 25 ++++++++++++++++++++++++- src/llvm_backend.cpp | 6 +++++- src/llvm_backend_expr.cpp | 17 ++++++++++++++--- src/llvm_backend_type.cpp | 2 ++ src/llvm_backend_utility.cpp | 30 ++++++++++++++++++++++-------- src/main.cpp | 7 +++++++ 11 files changed, 95 insertions(+), 19 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d6cdd7006..b2d6c4f43 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -277,6 +277,8 @@ struct BuildContext { bool copy_file_contents; + bool disallow_rtti; + RelocMode reloc_mode; bool disable_red_zone; @@ -946,7 +948,6 @@ void init_build_context(TargetMetrics *cross_target) { } } - bc->copy_file_contents = true; TargetMetrics *metrics = nullptr; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index eb9d7f293..5561da01b 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1241,6 +1241,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (c->scope->flags&ScopeFlag_Global) { compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } + if (build_context.disallow_rtti) { + error(call, "'%.*s' has been disallowed", LIT(builtin_name)); + return false; + } // NOTE(bill): The type information may not be setup yet init_core_type_info(c->checker); @@ -1253,9 +1257,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *t = o.type; if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) { if (is_type_polymorphic(t)) { - error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type"); + error(ce->args[0], "Invalid argument for '%.*s', unspecialized polymorphic type", LIT(builtin_name)); } else { - error(ce->args[0], "Invalid argument for 'type_info_of'"); + error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); } return false; } @@ -1266,7 +1270,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_operand_value(o) && is_type_typeid(t)) { add_package_dependency(c, "runtime", "__type_info_of"); } else if (o.mode != Addressing_Type) { - error(expr, "Expected a type or typeid for 'type_info_of'"); + error(expr, "Expected a type or typeid for '%.*s'", LIT(builtin_name)); return false; } @@ -1280,6 +1284,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (c->scope->flags&ScopeFlag_Global) { compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } + if (build_context.disallow_rtti) { + error(call, "'%.*s' has been disallowed", LIT(builtin_name)); + return false; + } // NOTE(bill): The type information may not be setup yet init_core_type_info(c->checker); @@ -1291,7 +1299,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } Type *t = o.type; if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) { - error(ce->args[0], "Invalid argument for 'typeid_of'"); + error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); return false; } t = default_type(t); @@ -1299,7 +1307,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_type_info_type(c, t); if (o.mode != Addressing_Type) { - error(expr, "Expected a type for 'typeid_of'"); + error(expr, "Expected a type for '%.*s'", LIT(builtin_name)); return false; } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3fdd944f9..b3b1e4474 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1205,6 +1205,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + + check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e07dc3d60..f1bcb4cd9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9352,6 +9352,8 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi if (o->type != nullptr && is_type_untyped(o->type)) { add_untyped(c, node, o->mode, o->type, o->value); } + check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed"); + add_type_and_value(c->info, node, o->mode, o->type, o->value); return kind; } diff --git a/src/check_type.cpp b/src/check_type.cpp index c2324ee5a..ff2c3d6a6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3031,5 +3031,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) { } set_base_type(named_type, type); + check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed"); + return type; } diff --git a/src/checker.cpp b/src/checker.cpp index fe1d362fa..e6445d752 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -29,6 +29,23 @@ bool is_operand_undef(Operand o) { return o.mode == Addressing_Value && o.type == t_untyped_undef; } +bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) { + if (build_context.disallow_rtti && type) { + if (is_type_any(type)) { + gbString t = type_to_string(type); + error(token, format, t); + gb_string_free(t); + return true; + } + } + return false; +} + +bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *format) { + GB_ASSERT(expr != nullptr); + return check_rtti_type_disallowed(ast_token(expr), type, format); +} + void scope_reset(Scope *scope) { if (scope == nullptr) return; @@ -875,7 +892,8 @@ void init_universal(void) { // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { - add_global_type_entity(basic_types[i].Basic.name, &basic_types[i]); + String const &name = basic_types[i].Basic.name; + add_global_type_entity(name, &basic_types[i]); } add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]); @@ -977,6 +995,7 @@ void init_universal(void) { add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point); add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES); + add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti); // Builtin Procedures @@ -1669,6 +1688,10 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) { void add_type_info_type(CheckerContext *c, Type *t) { void add_type_info_type_internal(CheckerContext *c, Type *t); + if (build_context.disallow_rtti) { + return; + } + mutex_lock(&c->info->type_info_mutex); add_type_info_type_internal(c, t); mutex_unlock(&c->info->type_info_mutex); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6ca256c4b..04c3200f8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1285,7 +1285,11 @@ void lb_generate_code(lbGenerator *gen) { // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL if (ODIN_LLVM_MINIMUM_VERSION_12) { - llvm_cpu = "x86-64-v2"; + if (build_context.metrics.os == TargetOs_freestanding) { + llvm_cpu = "x86-64"; + } else { + llvm_cpu = "x86-64-v2"; + } } } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 844deb43c..18b66572d 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2809,16 +2809,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); dst_tag = lb_const_union_tag(p->module, src_type, dst_type); } + + + isize arg_count = 6; + if (build_context.disallow_rtti) { + arg_count = 4; + } + lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); - auto args = array_make(permanent_allocator(), 6); + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); args[2] = lb_const_int(p->module, t_i32, pos.line); args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = lb_typeid(p->module, src_type); - args[5] = lb_typeid(p->module, dst_type); + if (!build_context.disallow_rtti) { + args[4] = lb_typeid(p->module, src_type); + args[5] = lb_typeid(p->module, dst_type); + } lb_emit_runtime_call(p, "type_assertion_check", args); } @@ -2831,6 +2840,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { } lbValue data_ptr = lb_emit_struct_ev(p, v, 0); if ((p->state_flags & StateFlag_no_type_assert) == 0) { + GB_ASSERT(!build_context.disallow_rtti); + lbValue any_id = lb_emit_struct_ev(p, v, 1); lbValue id = lb_typeid(p->module, type); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 1d6297164..1aac75f9c 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -14,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr } lbValue lb_typeid(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + type = default_type(type); u64 id = cast(u64)lb_type_info_index(m->info, type); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 98b7e07f0..fb52a9bd6 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -676,17 +676,24 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p // NOTE(bill): Panic on invalid conversion Type *dst_type = tuple->Tuple.variables[0]->type; + isize arg_count = 7; + if (build_context.disallow_rtti) { + arg_count = 4; + } + lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = lb_typeid(m, src_type); - args[5] = lb_typeid(m, dst_type); - args[6] = lb_emit_conv(p, value_, t_rawptr); + if (!build_context.disallow_rtti) { + args[4] = lb_typeid(m, src_type); + args[5] = lb_typeid(m, dst_type); + args[6] = lb_emit_conv(p, value_, t_rawptr); + } lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); @@ -744,16 +751,23 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos if (!is_tuple) { // NOTE(bill): Panic on invalid conversion lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); + + isize arg_count = 7; + if (build_context.disallow_rtti) { + arg_count = 4; + } + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = any_typeid; - args[5] = dst_typeid; - args[6] = lb_emit_struct_ev(p, value, 0);; + if (!build_context.disallow_rtti) { + args[4] = any_typeid; + args[5] = dst_typeid; + args[6] = lb_emit_struct_ev(p, value, 0); + } lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_addr(lb_emit_struct_ep(p, v.addr, 0)); diff --git a/src/main.cpp b/src/main.cpp index 8f4155d39..d2263f5a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -638,6 +638,7 @@ enum BuildFlagKind { BuildFlag_StrictStyle, BuildFlag_StrictStyleInitOnly, BuildFlag_ForeignErrorProcedures, + BuildFlag_DisallowRTTI, BuildFlag_Compact, BuildFlag_GlobalDefinitions, @@ -796,6 +797,9 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check); + + add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); @@ -1390,6 +1394,9 @@ bool parse_build_flags(Array args) { case BuildFlag_DisallowDo: build_context.disallow_do = true; break; + case BuildFlag_DisallowRTTI: + build_context.disallow_rtti = true; + break; case BuildFlag_DefaultToNilAllocator: build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; break; -- cgit v1.2.3 From 49fecbdc5e6579956346e484a418b9501dfddd71 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 1 Mar 2022 14:49:05 +0000 Subject: Improve error message when there is "no field" found for a large anonymous struct --- src/check_builtin.cpp | 8 ++++---- src/check_expr.cpp | 8 ++++---- src/types.cpp | 37 +++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 22 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 69cc40de8..aeeeb9e4d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1106,7 +1106,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(field_name)); gb_string_free(type_str); @@ -1118,7 +1118,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (sel.indirect) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); gb_string_free(type_str); @@ -1179,7 +1179,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(field_name)); gb_string_free(type_str); @@ -1191,7 +1191,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (sel.indirect) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); gb_string_free(type_str); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e07dc3d60..614da2368 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4470,7 +4470,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (entity == nullptr) { gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); @@ -4511,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4536,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4549,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) { gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str); gb_string_free(sel_str); diff --git a/src/types.cpp b/src/types.cpp index 74080334a..58ccdf5b9 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -703,7 +703,7 @@ struct TypePath; i64 type_size_of (Type *t); i64 type_align_of (Type *t); i64 type_offset_of (Type *t, i32 index); -gbString type_to_string (Type *type); +gbString type_to_string (Type *type, bool shorthand=false); i64 type_size_of_internal(Type *t, TypePath *path); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); @@ -3936,7 +3936,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type -gbString write_type_to_string(gbString str, Type *type) { +gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { return gb_string_appendc(str, ""); } @@ -4051,15 +4051,21 @@ gbString write_type_to_string(gbString str, Type *type) { if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); 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 (shorthand && type->Struct.fields.count > 16) { + str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count); + } else { + 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_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_append_rune(str, '}'); } break; @@ -4234,13 +4240,16 @@ gbString write_type_to_string(gbString str, Type *type) { } -gbString type_to_string(Type *type, gbAllocator allocator) { - return write_type_to_string(gb_string_make(allocator, ""), type); +gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) { + return write_type_to_string(gb_string_make(allocator, ""), type, shorthand); } -gbString type_to_string(Type *type) { - return write_type_to_string(gb_string_make(heap_allocator(), ""), type); +gbString type_to_string(Type *type, bool shorthand) { + return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand); } +gbString type_to_string_shorthand(Type *type) { + return type_to_string(type, true); +} -- cgit v1.2.3 From a68f0b2d72a3883879da124321868afcdac3b9d6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 18 Mar 2022 22:18:12 +0000 Subject: Improve procedure group selection based on the minimum number of arguments --- src/check_expr.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++----------- src/check_type.cpp | 14 ---------- src/checker.cpp | 9 +++++++ src/entity.cpp | 10 +++++++ src/types.cpp | 1 - 5 files changed, 82 insertions(+), 31 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 577f3b07c..b7039fd9a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4872,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) { return false; } - -CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { - ast_node(ce, CallExpr, call); - GB_ASSERT(is_type_proc(proc_type)); - proc_type = base_type(proc_type); - TypeProc *pt = &proc_type->Proc; - +isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) { + GB_ASSERT(pt != nullptr); + GB_ASSERT(pt->kind == Type_Proc); isize param_count = 0; isize param_count_excluding_defaults = 0; - bool variadic = pt->variadic; - bool vari_expand = (ce->ellipsis.pos.line != 0); - i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - - + bool variadic = pt->Proc.variadic; TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; + if (pt->Proc.params != nullptr) { + param_tuple = &pt->Proc.params->Tuple; param_count = param_tuple->variables.count; if (variadic) { @@ -4930,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } + if (param_count_) *param_count_ = param_count; + return param_count_excluding_defaults; +} + + +CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { + ast_node(ce, CallExpr, call); + GB_ASSERT(is_type_proc(proc_type)); + proc_type = base_type(proc_type); + TypeProc *pt = &proc_type->Proc; + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); + bool variadic = pt->variadic; + bool vari_expand = (ce->ellipsis.pos.line != 0); + i64 score = 0; + bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + + + TypeTuple *param_tuple = nullptr; + if (pt->params != nullptr) { + param_tuple = &pt->params->Tuple; + } + + CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5602,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (operand->mode == Addressing_ProcGroup) { check_entity_decl(c, operand->proc_group, nullptr, nullptr); - Array procs = proc_group_entities(c, *operand); + auto procs = proc_group_entities_cloned(c, *operand); + + if (procs.count > 1) { + isize max_arg_count = args.count; + for_array(i, args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + Ast *arg = strip_or_return_expr(args[i]); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + + for (isize proc_index = 0; proc_index < procs.count; /**/) { + Entity *proc = procs[proc_index]; + Type *pt = base_type(proc->type); + if (!(pt != nullptr && is_type_proc(pt))) { + continue; + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + + if (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + } else { + proc_index++; + } + } + } if (procs.count == 1) { Ast *ident = operand->expr; @@ -5632,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type return data; } + Entity **lhs = nullptr; isize lhs_count = -1; diff --git a/src/check_type.cpp b/src/check_type.cpp index ecb2c26ea..3c7f33a46 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1960,20 +1960,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, if (params) param_count = params ->Tuple.variables.count; if (results) result_count = results->Tuple.variables.count; - if (param_count > 0) { - for_array(i, params->Tuple.variables) { - Entity *param = params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - ParameterValue pv = param->Variable.param_value; - if (pv.kind == ParameterValue_Constant && - pv.value.kind == ExactValue_Procedure) { - type->Proc.has_proc_default_values = true; - break; - } - } - } - } - if (result_count > 0) { Entity *first = results->Tuple.variables[0]; type->Proc.has_named_results = first->token.string != ""; diff --git a/src/checker.cpp b/src/checker.cpp index ea300afd9..53bba654d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2657,6 +2657,15 @@ Array proc_group_entities(CheckerContext *c, Operand o) { return procs; } +Array proc_group_entities_cloned(CheckerContext *c, Operand o) { + auto entities = proc_group_entities(c, o); + if (entities.count == 0) { + return {}; + } + return array_clone(permanent_allocator(), entities); +} + + void init_core_type_info(Checker *c) { diff --git a/src/entity.cpp b/src/entity.cpp index f5720293f..17fa884e1 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -115,6 +115,16 @@ struct ParameterValue { }; }; +bool has_parameter_value(ParameterValue const ¶m_value) { + if (param_value.kind != ParameterValue_Invalid) { + return true; + } + if (param_value.original_ast_expr != nullptr) { + return true; + } + return false; +} + enum EntityConstantFlags : u32 { EntityConstantFlag_ImplicitEnumValue = 1<<0, }; diff --git a/src/types.cpp b/src/types.cpp index 58ccdf5b9..ef770c846 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -186,7 +186,6 @@ struct TypeProc { bool c_vararg; bool is_polymorphic; bool is_poly_specialized; - bool has_proc_default_values; bool has_named_results; bool diverging; // no return bool return_by_pointer; -- cgit v1.2.3 From 10c58257159f8c8d046dd3cb93dde6b404414169 Mon Sep 17 00:00:00 2001 From: gitlost Date: Wed, 23 Mar 2022 12:56:37 +0000 Subject: Fix issue #829 "Compiler crashes when declaring maps with procedure" Inits `o->value` in `check_expr_base_internal()` so doesn't accidentally use last (the proc lit was being set to that of previous string) Adds test to "tests/issues" and changes CI to use new "run" shells --- .github/workflows/ci.yml | 6 +++--- src/check_expr.cpp | 1 + tests/issues/run.bat | 17 +++++++++++++++++ tests/issues/run.sh | 18 ++++++++++++++++++ tests/issues/test_issue_829.odin | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tests/issues/run.bat create mode 100755 tests/issues/run.sh create mode 100644 tests/issues/test_issue_829.odin (limited to 'src/check_expr.cpp') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3b1d8058..3cc4283b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: make timeout-minutes: 10 - name: Odin issues tests - run: ./odin run tests/issues -collection:tests=tests + run: tests/issues/run.sh timeout-minutes: 10 - name: Odin check examples/all for Linux i386 run: ./odin check examples/all -vet -strict-style -target:linux_i386 @@ -91,7 +91,7 @@ jobs: make timeout-minutes: 10 - name: Odin issues tests - run: ./odin run tests/issues -collection:tests=tests + run: tests/issues/run.sh timeout-minutes: 10 - name: Odin check examples/all for Darwin arm64 run: ./odin check examples/all -vet -strict-style -target:darwin_arm64 @@ -163,7 +163,7 @@ jobs: shell: cmd run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin run tests\issues -collection:tests=tests + call tests\issues\run.bat timeout-minutes: 10 - name: Odin check examples/all for Windows 32bits shell: cmd diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b7039fd9a..66bf8bbd7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9000,6 +9000,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->mode = Addressing_Invalid; o->type = t_invalid; + o->value = {ExactValue_Invalid}; switch (node->kind) { default: diff --git a/tests/issues/run.bat b/tests/issues/run.bat new file mode 100644 index 000000000..b145dda48 --- /dev/null +++ b/tests/issues/run.bat @@ -0,0 +1,17 @@ +@echo off + +if not exist "tests\issues\build\" mkdir tests\issues\build + +set COMMON=-collection:tests=tests -out:tests\issues\build\test_issue + +@echo on + +.\odin build tests\issues\test_issue_829.odin %COMMON% +tests\issues\build\test_issue + +.\odin build tests\issues\test_issue_1592.odin %COMMON% +tests\issues\build\test_issue + +@echo off + +rmdir /S /Q tests\issues\build diff --git a/tests/issues/run.sh b/tests/issues/run.sh new file mode 100755 index 000000000..c4f978771 --- /dev/null +++ b/tests/issues/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -eu + +mkdir -p tests/issues/build + +COMMON="-collection:tests=tests -out:tests/issues/build/test_issue" + +set -x + +./odin build tests/issues/test_issue_829.odin $COMMON +tests/issues/build/test_issue + +./odin build tests/issues/test_issue_1592.odin $COMMON +tests/issues/build/test_issue + +set +x + +rm -rf tests/issues/build diff --git a/tests/issues/test_issue_829.odin b/tests/issues/test_issue_829.odin new file mode 100644 index 000000000..4ff3d71f1 --- /dev/null +++ b/tests/issues/test_issue_829.odin @@ -0,0 +1,33 @@ +// Tests issue #829 https://github.com/odin-lang/Odin/issues/829 +package test_issues + +import "core:fmt" +import "core:testing" +import tc "tests:common" + +/* Original issue #829 example */ + +env : map[string]proc(a, b : int) -> int = { + "+" = proc(a, b : int) -> int { + return a + b + }, +} + +test_orig :: proc() { + fmt.println(env["+"](1, 2)) +} + +main :: proc() { + t := testing.T{} + + test_orig() + + test_orig_ret(&t) + + tc.report(&t) +} + +test_orig_ret :: proc(t: ^testing.T) { + r := fmt.tprint(env["+"](1, 2)) + tc.expect(t, r == "3", fmt.tprintf("%s: \"%s\" != \"3\"\n", #procedure, r)) +} -- cgit v1.2.3 From 3f935bea2505b3ee7e169a29b7aed50c0e5614b7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 24 Mar 2022 11:55:03 +0000 Subject: `union #shared_nil` This adds a feature to `union` which requires all the variants to have a `nil` value and on assign to the union, checks whether that value is `nil` or not. If the value is `nil`, the union will be `nil` (thus sharing the `nil` value) --- core/runtime/core.odin | 1 + src/check_expr.cpp | 7 +++++-- src/check_type.cpp | 19 ++++++++++++++----- src/docs_format.cpp | 1 + src/docs_writer.cpp | 8 +++++--- src/llvm_backend_general.cpp | 31 ++++++++++++++++++++++++++++--- src/llvm_backend_type.cpp | 7 ++++--- src/parser.cpp | 30 ++++++++++++++++++++++++++---- src/parser.hpp | 10 ++++++++-- src/types.cpp | 22 ++++++++++++---------- 10 files changed, 104 insertions(+), 32 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 8c95a234f..a5a190a9c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -136,6 +136,7 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, maybe: bool, + shared_nil: bool, } Type_Info_Enum :: struct { base: ^Type_Info, diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 66bf8bbd7..dcf17af39 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10047,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, st->polymorphic_params, shorthand); str = gb_string_appendc(str, ") "); } - if (st->no_nil) str = gb_string_appendc(str, "#no_nil "); - if (st->maybe) str = gb_string_appendc(str, "#maybe "); + switch (st->kind) { + case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break; + case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; + case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; + } if (st->align) { str = gb_string_appendc(str, "#align "); str = write_expr_to_string(str, st->align, shorthand); diff --git a/src/check_type.cpp b/src/check_type.cpp index 3c7f33a46..51f472961 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -675,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) { + if (!type_has_nil(t)) { + gbString s = type_to_string(t); + error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s); + gb_string_free(s); + } + } } } } union_type->Union.variants = slice_from_array(variants); - union_type->Union.no_nil = ut->no_nil; - union_type->Union.maybe = ut->maybe; - if (union_type->Union.no_nil) { + union_type->Union.kind = ut->kind; + switch (ut->kind) { + case UnionType_no_nil: if (variants.count < 2) { error(ut->align, "A union with #no_nil must have at least 2 variants"); } - } - if (union_type->Union.maybe) { + break; + case UnionType_maybe: if (variants.count != 1) { error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count); } + break; } if (ut->align != nullptr) { diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 7ce93d2bf..ee32d0e05 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 { OdinDocTypeFlag_Union_polymorphic = 1<<0, OdinDocTypeFlag_Union_no_nil = 1<<1, OdinDocTypeFlag_Union_maybe = 1<<2, + OdinDocTypeFlag_Union_shared_nil = 1<<3, }; enum OdinDocTypeFlag_Proc : u32 { diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 2c5186c39..0ad10ac49 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -619,9 +619,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { case Type_Union: doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } - if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; } - if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; } - + switch (type->Union.kind) { + case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break; + case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; + case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; + } { auto variants = array_make(heap_allocator(), type->Union.variants.count); defer (array_free(&variants)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index dd1555193..f6dd97966 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1176,10 +1176,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia } void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) { - lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + Type *pt = base_type(type_deref(parent.type)); + GB_ASSERT(pt->kind == Type_Union); + if (pt->Union.kind == UnionType_shared_nil) { + lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil"); + lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil"); + lbBlock *done = lb_create_block(p, "shared_nil.done"); + + lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant); + lb_emit_if(p, cond_is_nil, if_nil, if_not_nil); + + lb_start_block(p, if_nil); + lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type))); + lb_emit_jump(p, done); + + lb_start_block(p, if_not_nil); + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + lb_emit_jump(p, done); + + lb_start_block(p, done); - lb_emit_store(p, underlying, variant); - lb_emit_store_union_variant_tag(p, parent, variant_type); + + } else { + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + } } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e245a8b40..7d73956e8 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[7] = {}; + LLVMValueRef vals[8] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -675,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value; + vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value; + vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index a435d1317..767119aa8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1071,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_c } -Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, +Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = slice_from_array(variants); result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; - result->UnionType.no_nil = no_nil; - result->UnionType.maybe = maybe; + result->UnionType.kind = kind; result->UnionType.where_token = where_token; result->UnionType.where_clauses = slice_from_array(where_clauses); return result; @@ -2475,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { Ast *align = nullptr; bool no_nil = false; bool maybe = false; + bool shared_nil = false; + + UnionTypeKind union_kind = UnionType_Normal; Token start_token = f->curr_token; @@ -2501,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); } no_nil = true; + } else if (tag.string == "shared_nil") { + if (shared_nil) { + syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); + } + shared_nil = true; } else if (tag.string == "maybe") { if (maybe) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); @@ -2513,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (no_nil && maybe) { syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together"); } + if (no_nil && shared_nil) { + syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together"); + } + if (shared_nil && maybe) { + syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together"); + } + + + if (maybe) { + union_kind = UnionType_maybe; + } else if (no_nil) { + union_kind = UnionType_no_nil; + } else if (shared_nil) { + union_kind = UnionType_shared_nil; + } skip_possible_newline_for_literal(f); @@ -2544,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_closing_brace_of_field_list(f); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses); + return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses); } break; case Token_enum: { diff --git a/src/parser.hpp b/src/parser.hpp index c33d1520b..c7b4fd0d8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -330,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { "intel", }; +enum UnionTypeKind : u8 { + UnionType_Normal = 0, + UnionType_maybe = 1, + UnionType_no_nil = 2, + UnionType_shared_nil = 3, +}; + #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ @@ -678,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \ Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ - bool maybe; \ - bool no_nil; \ + UnionTypeKind kind; \ Token where_token; \ Slice where_clauses; \ }) \ diff --git a/src/types.cpp b/src/types.cpp index ef770c846..b231218a2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -165,9 +165,8 @@ struct TypeUnion { i16 tag_size; bool is_polymorphic; - bool is_poly_specialized : 1; - bool no_nil : 1; - bool maybe : 1; + bool is_poly_specialized; + UnionTypeKind kind; }; struct TypeProc { @@ -1664,7 +1663,7 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { + if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { if (t->Union.variants.count == 1) { Type *v = t->Union.variants[0]; return is_type_pointer(v) || is_type_multi_pointer(v); @@ -1676,7 +1675,7 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { + if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { if (t->Union.variants.count == 1) { Type *v = t->Union.variants[0]; if (is_type_pointer(v) || is_type_multi_pointer(v)) { @@ -2168,7 +2167,7 @@ bool type_has_nil(Type *t) { case Type_Map: return true; case Type_Union: - return !t->Union.no_nil; + return t->Union.kind != UnionType_no_nil; case Type_Struct: if (is_type_soa_struct(t)) { switch (t->Struct.soa_kind) { @@ -2454,7 +2453,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (y->kind == Type_Union) { if (x->Union.variants.count == y->Union.variants.count && x->Union.custom_align == y->Union.custom_align && - x->Union.no_nil == y->Union.no_nil) { + x->Union.kind == y->Union.kind) { // NOTE(bill): zeroth variant is nullptr for_array(i, x->Union.variants) { if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) { @@ -2598,7 +2597,7 @@ i64 union_variant_index(Type *u, Type *v) { for_array(i, u->Union.variants) { Type *vt = u->Union.variants[i]; if (are_types_identical(v, vt)) { - if (u->Union.no_nil) { + if (u->Union.kind == UnionType_no_nil) { return cast(i64)(i+0); } else { return cast(i64)(i+1); @@ -4021,8 +4020,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { case Type_Union: str = gb_string_appendc(str, "union"); - if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil"); - if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe"); + switch (type->Union.kind) { + case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break; + case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; + case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; + } if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align); str = gb_string_appendc(str, " {"); for_array(i, type->Union.variants) { -- cgit v1.2.3 From 8a9f7fc684b39c803cab184344a8a017308ed63a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 14 Apr 2022 15:09:03 +0100 Subject: Fix #1713 --- src/check_expr.cpp | 5 ++++- src/exact_value.cpp | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index dcf17af39..336a711d4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8161,7 +8161,10 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * case Type_Basic: { if (!is_type_any(t)) { if (cl->elems.count != 0) { - error(node, "Illegal compound literal"); + gbString s = type_to_string(t); + error(node, "Illegal compound literal, %s cannot be used as a compound literal with fields", s); + gb_string_free(s); + is_constant = false; } break; } diff --git a/src/exact_value.cpp b/src/exact_value.cpp index f6df48951..cedef48c4 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -591,6 +591,7 @@ failure: i32 exact_value_order(ExactValue const &v) { switch (v.kind) { case ExactValue_Invalid: + case ExactValue_Compound: return 0; case ExactValue_Bool: case ExactValue_String: @@ -607,8 +608,6 @@ i32 exact_value_order(ExactValue const &v) { return 6; case ExactValue_Procedure: return 7; - // case ExactValue_Compound: - // return 8; default: GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind); -- cgit v1.2.3 From dca2fbccffd22b47cdb89e56170f3dae9a6257ad Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 11 May 2022 12:15:10 +0100 Subject: Improve ternary if type inference --- src/check_expr.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 336a711d4..f578f8c73 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7241,7 +7241,11 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t node->viral_state_flags |= te->x->viral_state_flags; if (te->y != nullptr) { - check_expr_or_type(c, &y, te->y, type_hint); + Type *th = type_hint; + if (type_hint == nullptr && is_type_typed(x.type)) { + th = x.type; + } + check_expr_or_type(c, &y, te->y, th); node->viral_state_flags |= te->y->viral_state_flags; } else { error(node, "A ternary expression must have an else clause"); -- cgit v1.2.3 From 438713af2037511208b52545fd43a6155c017b34 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 May 2022 11:33:52 +0100 Subject: Allow `transmute` on constant expressions --- src/check_expr.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f578f8c73..bc28583b7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2786,14 +2786,14 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) { return false; } - if (o->mode == Addressing_Constant) { - gbString expr_str = expr_to_string(o->expr); - error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str); - gb_string_free(expr_str); - o->mode = Addressing_Invalid; - o->expr = node; - return false; - } + // if (o->mode == Addressing_Constant) { + // gbString expr_str = expr_to_string(o->expr); + // error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str); + // gb_string_free(expr_str); + // o->mode = Addressing_Invalid; + // o->expr = node; + // return false; + // } if (is_type_untyped(o->type)) { gbString expr_str = expr_to_string(o->expr); -- cgit v1.2.3 From d9f293b2818b30ebe2d29180aa62c3ce432c4582 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 May 2022 11:50:05 +0100 Subject: Add better error message for trying to dereference a multi-pointer --- src/check_expr.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bc28583b7..013d22913 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9310,7 +9310,15 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } else { gbString str = expr_to_string(o->expr); gbString typ = type_to_string(o->type); + begin_error_block(); + error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ); + if (o->type && is_type_multi_pointer(o->type)) { + error_line("\tDid you mean '%s[0]'?\n", str); + } + + end_error_block(); + gb_string_free(typ); gb_string_free(str); o->mode = Addressing_Invalid; -- cgit v1.2.3 From 3ec70c5517062f3d35822253b2072df696b0c55f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 May 2022 12:04:19 +0100 Subject: Merge functionality of `#maybe` with the standard 'union' functionality --- core/encoding/json/unmarshal.odin | 2 +- core/reflect/reflect.odin | 2 +- core/reflect/types.odin | 16 +++++++++++++--- core/runtime/core.odin | 1 - core/runtime/core_builtin.odin | 2 +- src/check_expr.cpp | 1 - src/docs_writer.cpp | 1 - src/llvm_backend_type.cpp | 5 ++--- src/parser.cpp | 1 + src/parser.hpp | 2 +- src/types.cpp | 19 +++++++------------ 11 files changed, 27 insertions(+), 25 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index bd48011f1..2ff268a21 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { variant := u.variants[0] v.id = variant.id ti = reflect.type_info_base(variant) - if !(u.maybe && reflect.is_pointer(variant)) { + if !reflect.is_pointer_internally(variant) { tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id} assign_int(tag, 1) } diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 49d7ef9b5..27a83e680 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -654,7 +654,7 @@ union_variant_type_info :: proc(a: any) -> ^Type_Info { } type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool { - return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]) + return len(info.variants) == 1 && is_pointer(info.variants[0]) } union_variant_typeid :: proc(a: any) -> typeid { diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2e2149820..b211abb45 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -256,6 +256,17 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer) return ok } +is_pointer_internally :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + #partial switch v in info.variant { + case Type_Info_Pointer, Type_Info_Multi_Pointer, + Type_Info_Procedure: + return true + case Type_Info_String: + return v.is_cstring + } + return false +} is_procedure :: proc(info: ^Type_Info) -> bool { if info == nil { return false } _, ok := type_info_base(info).variant.(Type_Info_Procedure) @@ -531,9 +542,8 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - case Type_Info_Union: io.write_string(w, "union ", &n) or_return - if info.maybe { - io.write_string(w, "#maybe ", &n) or_return - } + if info.no_nil { io.write_string(w, "#no_nil ", &n) or_return } + if info.shared_nil { io.write_string(w, "#shared_nil ", &n) or_return } if info.custom_align { io.write_string(w, "#align ", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return diff --git a/core/runtime/core.odin b/core/runtime/core.odin index e2933d20a..73d1e6371 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -135,7 +135,6 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, - maybe: bool, shared_nil: bool, } Type_Info_Enum :: struct { diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index cb72e397d..4ddc3928a 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -3,7 +3,7 @@ package runtime import "core:intrinsics" @builtin -Maybe :: union($T: typeid) #maybe {T} +Maybe :: union($T: typeid) {T} @builtin diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 013d22913..b911771b1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10063,7 +10063,6 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = gb_string_appendc(str, ") "); } switch (st->kind) { - case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break; case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; } diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 0ad10ac49..2f531a45c 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -620,7 +620,6 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } switch (type->Union.kind) { - case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break; case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 7d73956e8..2e7b2788a 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[8] = {}; + LLVMValueRef vals[7] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -676,8 +676,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value; - vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index ab947774b..d19e249e5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2529,6 +2529,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (maybe) { union_kind = UnionType_maybe; + syntax_error(f->curr_token, "#maybe functionality has now been merged with standard 'union' functionality"); } else if (no_nil) { union_kind = UnionType_no_nil; } else if (shared_nil) { diff --git a/src/parser.hpp b/src/parser.hpp index 698ed7623..dc294b6ce 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -332,7 +332,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { enum UnionTypeKind : u8 { UnionType_Normal = 0, - UnionType_maybe = 1, + UnionType_maybe = 1, // removed UnionType_no_nil = 2, UnionType_shared_nil = 3, }; diff --git a/src/types.cpp b/src/types.cpp index b4dc17256..c79b8e652 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1685,11 +1685,9 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - return is_type_pointer(v) || is_type_multi_pointer(v); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + return is_type_internally_pointer_like(v); } return false; } @@ -1697,12 +1695,10 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - if (is_type_pointer(v) || is_type_multi_pointer(v)) { - return type_align_of(v) == type_align_of(t); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + if (is_type_internally_pointer_like(v)) { + return type_align_of(v) == type_align_of(t); } } return false; @@ -4054,7 +4050,6 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { case Type_Union: str = gb_string_appendc(str, "union"); switch (type->Union.kind) { - case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break; case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; } -- cgit v1.2.3 From 084f431aa510793a6e2011816efebbc71a24c0ff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 May 2022 12:19:33 +0100 Subject: Correct `check_transmute` operand logic --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 013d22913..c674a774e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2844,8 +2844,10 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) { } } + o->expr = node; o->mode = Addressing_Value; o->type = t; + o->value = {}; return true; } -- cgit v1.2.3 From 3d9d85121d207b41e24214c4acc52fc50271127f Mon Sep 17 00:00:00 2001 From: Cedric Hutchings Date: Mon, 23 May 2022 08:14:05 -0400 Subject: Clear up Mismatched BE types error message --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2bb79e21c..7b269e048 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3211,7 +3211,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint y->type != t_invalid) { gbString xt = type_to_string(x->type); gbString yt = type_to_string(y->type); - gbString expr_str = expr_to_string(x->expr); + gbString expr_str = expr_to_string(node); error(op, "Mismatched types in binary expression '%s' : '%s' vs '%s'", expr_str, xt, yt); gb_string_free(expr_str); gb_string_free(yt); -- cgit v1.2.3 From b032d5af87ebe8d9dee28698cfa570d3628e58e5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 17:26:18 +0100 Subject: Make `#simd` an opaque type --- src/check_decl.cpp | 20 +++++++++++++------- src/check_expr.cpp | 24 ++++++++---------------- src/check_type.cpp | 5 +++++ src/common.cpp | 7 +++++++ src/types.cpp | 2 +- 5 files changed, 34 insertions(+), 24 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 82ac6c677..d8cad2ce1 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -313,13 +313,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) } named->Named.base = base; - if (is_distinct && is_type_typeid(e->type)) { - error(init_expr, "'distinct' cannot be applied to 'typeid'"); - is_distinct = false; - } - if (is_distinct && is_type_any(e->type)) { - error(init_expr, "'distinct' cannot be applied to 'any'"); - is_distinct = false; + if (is_distinct) { + if (is_type_typeid(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'typeid'"); + is_distinct = false; + } else if (is_type_any(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'any'"); + is_distinct = false; + } else if (is_type_simd_vector(e->type)) { + gbString str = type_to_string(e->type); + error(init_expr, "'distinct' cannot be applied to '%s'", str); + gb_string_free(str); + is_distinct = false; + } } if (!is_distinct) { e->type = bt; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7b269e048..a4dfade98 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1567,9 +1567,16 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) { bool check_binary_op(CheckerContext *c, Operand *o, Token op) { Type *main_type = o->type; + + if (is_type_simd_vector(main_type)) { + error(op, "Operator '%.*s' is not supported on #simd vector types, please use the intrinsics.simd_*", LIT(op.string)); + return false; + } + // TODO(bill): Handle errors correctly Type *type = base_type(core_array_type(main_type)); Type *ct = core_type(type); + switch (op.kind) { case Token_Sub: case Token_SubEq: @@ -1638,14 +1645,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); return false; } - if (is_type_simd_vector(o->type)) { - switch (op.kind) { - case Token_ModMod: - case Token_ModModEq: - error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); - return false; - } - } break; case Token_AndNot: @@ -1654,14 +1653,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string)); return false; } - if (is_type_simd_vector(o->type)) { - switch (op.kind) { - case Token_AndNot: - case Token_AndNotEq: - error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); - return false; - } - } break; case Token_CmpAnd: @@ -7738,6 +7729,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * } if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): Why was this decision made for simd? if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { diff --git a/src/check_type.cpp b/src/check_type.cpp index 51f472961..193c42cde 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2802,6 +2802,11 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t *type = alloc_type_array(elem, count, generic_type); goto array_end; } + if (count < 1 || !is_power_of_two(count)) { + error(at->elem, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); + *type = alloc_type_array(elem, count, generic_type); + goto array_end; + } *type = alloc_type_simd_vector(count, elem); } else { diff --git a/src/common.cpp b/src/common.cpp index 94248fb62..77caddfe8 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -47,6 +47,13 @@ void debugf(char const *fmt, ...); #include "range_cache.cpp" +bool is_power_of_two(i64 x) { + if (x <= 0) { + return false; + } + return !(x & (x-1)); +} + int isize_cmp(isize x, isize y) { if (x < y) { return -1; diff --git a/src/types.cpp b/src/types.cpp index c79b8e652..d5ba1a531 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3446,7 +3446,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Type_SimdVector: { // IMPORTANT TODO(bill): Figure out the alignment of vector types - return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); + return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2); } case Type_Matrix: -- cgit v1.2.3 From f21e9ee71281ec8665a3009cd1a350ffde3b7046 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 18:59:47 +0100 Subject: Allow basic casting of simd vectors --- src/check_expr.cpp | 12 ++++++++++++ src/llvm_backend_expr.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a4dfade98..9a7cd5b8b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2688,6 +2688,18 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { + if (src->SimdVector.count != dst->SimdVector.count) { + return false; + } + Type *elem_src = base_array_type(src); + Type *elem_dst = base_array_type(dst); + Operand x = {}; + x.type = elem_src; + x.mode = Addressing_Value; + return check_is_castable_to(c, &x, elem_dst); + } + return false; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 133df4d41..f4b5702bb 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1820,6 +1820,38 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { + Type *src_elem = core_array_type(src); + Type *dst_elem = core_array_type(dst); + + GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); + + lbValue res = {}; + res.type = t; + if (are_types_identical(src_elem, dst_elem)) { + res.value = value.value; + } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { + if (is_type_unsigned(dst_elem)) { + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { + if (is_type_unsigned(src_elem)) { + res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_integer(dst_elem)) { + res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); + } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { + res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); + } else { + GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); + } + return res; + } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { lbValue res = {}; -- cgit v1.2.3 From 53f0c6ef1a73ab7afe21ed15d3d88a266af6a03c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 20:31:31 +0100 Subject: Add ranges for simd compounds literals --- src/check_expr.cpp | 164 ++++++++++++++++++++++----------------------- src/llvm_backend_const.cpp | 85 ++++++++++++++++++----- 2 files changed, 149 insertions(+), 100 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9a7cd5b8b..3dbecb1c0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7741,112 +7741,106 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * } if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { - // TODO(bill): Why was this decision made for simd? - if (is_type_simd_vector(t)) { - error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); - } else { - RangeCache rc = range_cache_make(heap_allocator()); - defer (range_cache_destroy(&rc)); + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); - if (is_ast_range(fv->field)) { - Token op = fv->field->BinaryExpr.op; + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; - Operand x = {}; - Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr); - if (!ok) { - continue; - } - if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { - error(x.expr, "Expected a constant integer as an array field"); - continue; - } + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } - if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { - error(y.expr, "Expected a constant integer as an array field"); - continue; - } + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } - i64 lo = exact_value_to_i64(x.value); - i64 hi = exact_value_to_i64(y.value); - i64 max_index = hi; - if (op.kind == Token_RangeHalf) { // ..< (exclusive) - hi -= 1; - } else { // .. (inclusive) - max_index += 1; - } + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { // ..< (exclusive) + hi -= 1; + } else { // .. (inclusive) + max_index += 1; + } - bool new_range = range_cache_add_range(&rc, lo, hi); - if (!new_range) { - error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); - continue; - } + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } - if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); - continue; - } - if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); - continue; - } + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } - if (max < hi) { - max = max_index; - } + if (max < hi) { + max = max_index; + } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); - is_constant = is_constant && operand.mode == Addressing_Constant; - } else { - Operand op_index = {}; - check_expr(c, &op_index, fv->field); + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } - // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value); + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } + // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value); - i64 index = exact_value_to_i64(op_index.value); + i64 index = exact_value_to_i64(op_index.value); - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } - bool new_index = range_cache_add_index(&rc, index); - if (!new_index) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } - if (max < index+1) { - max = index+1; - } + if (max < index+1) { + max = index+1; + } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); - is_constant = is_constant && operand.mode == Addressing_Constant; - } + is_constant = is_constant && operand.mode == Addressing_Constant; } - - cl->max_count = max; } + cl->max_count = max; } else { isize index = 0; for (; index < cl->elems.count; index++) { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 8f17a1cfb..3a3067dbc 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -819,26 +819,81 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } GB_ASSERT(elem_type_can_be_constant(elem_type)); - isize total_elem_count = cast(isize)type->SimdVector.count; LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count); - for (isize i = 0; i < elem_count; i++) { - TypeAndValue tav = cl->elems[i]->tav; - GB_ASSERT(tav.mode != Addressing_Invalid); - values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value; - } - LLVMTypeRef et = lb_type(m, elem_type); + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + isize value_index = 0; + for (i64 i = 0; i < total_elem_count; i++) { + bool found = false; - for (isize i = elem_count; i < type->SimdVector.count; i++) { - values[i] = LLVMConstNull(et); - } - for (isize i = 0; i < total_elem_count; i++) { - values[i] = llvm_const_cast(values[i], et); - } + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); - res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); - return res; + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value; + for (i64 k = lo; k < hi; k++) { + values[value_index++] = val; + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value; + values[value_index++] = val; + found = true; + break; + } + } + } + + if (!found) { + values[value_index++] = LLVMConstNull(lb_type(m, elem_type)); + } + } + + res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); + return res; + } else { + for (isize i = 0; i < elem_count; i++) { + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value; + } + LLVMTypeRef et = lb_type(m, elem_type); + + for (isize i = elem_count; i < total_elem_count; i++) { + values[i] = LLVMConstNull(et); + } + for (isize i = 0; i < total_elem_count; i++) { + values[i] = llvm_const_cast(values[i], et); + } + + res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); + return res; + } } else if (is_type_struct(type)) { ast_node(cl, CompoundLit, value.value_compound); -- cgit v1.2.3 From 0203bb657ed09046dddc958be5322b1cd8c0a589 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 20:39:22 +0100 Subject: Allow for non-constant simd vector compound types --- src/check_expr.cpp | 2 +- src/llvm_backend_expr.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3dbecb1c0..9fd6acefd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7885,7 +7885,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * if (t->kind == Type_SimdVector) { if (!is_constant) { - error(node, "Expected all constant elements for a simd vector"); + // error(node, "Expected all constant elements for a simd vector"); } } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index f4b5702bb..1b10cd776 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4641,6 +4641,102 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { break; } + case Type_SimdVector: { + if (cl->elems.count > 0) { + lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr)); + defer (lb_addr_store(p, v, vector_value)); + + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + + // NOTE(bill): Separate value, store into their own chunks + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (lb_is_elem_const(fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + + lbValue value = lb_build_expr(p, fv->value); + + for (i64 k = lo; k < hi; k++) { + lbCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + lbValue value = lb_build_expr(p, fv->value); + lbCompoundLitElemTempData data = {}; + data.value = lb_emit_conv(p, value, et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } + + } else { + if (lb_is_elem_const(elem, et)) { + continue; + } + lbCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); + } + } + + + for_array(i, temp_data) { + lbValue field_expr = temp_data[i].value; + Ast *expr = temp_data[i].expr; + + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); + + if (field_expr.value == nullptr) { + field_expr = lb_build_expr(p, expr); + } + Type *t = field_expr.type; + GB_ASSERT(t->kind != Type_Tuple); + lbValue ev = lb_emit_conv(p, field_expr, et); + + if (!p->copy_elision_hint.used) { + temp_data[i].value = ev; + } + + lb_reset_copy_elision_hint(p, prev_hint); + } + + + // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector` + // might be a better option + + for_array(i, temp_data) { + if (temp_data[i].value.value != nullptr) { + LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value; + vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, ""); + } + } + } + break; + } } return v; -- cgit v1.2.3 From 1549d01bf76e8c5e13626e57b1f976330a9cdd50 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 21:17:21 +0100 Subject: Restrict `swizzle` to a power of two for #simd --- src/check_builtin.cpp | 35 +++++++++++++++++++++++++++++++++++ src/check_expr.cpp | 6 +++++- src/check_type.cpp | 6 ++++-- 3 files changed, 44 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 64b2ebfce..69e584827 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -694,6 +694,36 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } break; + + // case BuiltinProc_simd_rotate_left: + // { + // Operand x = {}; + // check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + + // if (!is_type_simd_vector(x.type)) { + // error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + // return false; + // } + // Type *elem = base_array_type(x.type); + // if (!is_type_integer(elem) && !is_type_float(elem)) { + // gbString xs = type_to_string(x.type); + // error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + // gb_string_free(xs); + // return false; + // } + + // Operand offset = {}; + // check_expr_with_type_hint(c, &offset, ce->args[1]); if (x.mode == Addressing_Invalid) { return false; } + // convert_to_typed(c, &offset, t_int); + // if (offset.mode != Addressing_Constant) { + // error(offset.expr, "'%.*s' expected a constant integer for the offset", LIT(builtin_name)); + // return false; + // } + + // operand->mode = Addressing_Value; + // operand->type = x.type; + // return true + // } default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); } @@ -1749,6 +1779,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Value; } + if (is_type_simd_vector(type) && !is_power_of_two(arg_count)) { + error(call, "'swizzle' with a #simd vector must have a power of two arguments, got %lld", cast(long long)arg_count); + return false; + } + operand->type = determine_swizzle_array_type(original_type, type_hint, arg_count); break; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9fd6acefd..a30f83e7e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4119,7 +4119,11 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) { Type *array_type = base_type(type_deref(original_type)); - GB_ASSERT(array_type->kind == Type_Array); + GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector); + if (array_type->kind == Type_SimdVector) { + Type *elem_type = array_type->SimdVector.elem; + return alloc_type_simd_vector(new_count, elem_type); + } Type *elem_type = array_type->Array.elem; Type *swizzle_array_type = nullptr; diff --git a/src/check_type.cpp b/src/check_type.cpp index 1df63e599..088853810 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2795,14 +2795,16 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (name == "soa") { *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); } else if (name == "simd") { - if (!is_type_valid_vector_elem(elem)) { + if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { gbString str = type_to_string(elem); error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str); gb_string_free(str); *type = alloc_type_array(elem, count, generic_type); goto array_end; } - if (count < 1 || !is_power_of_two(count)) { + if (is_type_polymorphic(elem)) { + count = 1; + } else if (count < 1 || !is_power_of_two(count)) { error(at->count, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); *type = alloc_type_array(elem, count, generic_type); goto array_end; -- cgit v1.2.3 From 63cc8a80a045d48960d85640d11f39237c2f8ca4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 21:29:45 +0100 Subject: Correct parapoly for #simd --- src/check_expr.cpp | 13 +++++++++++++ src/check_type.cpp | 4 ++-- src/types.cpp | 9 ++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a30f83e7e..fcd7818bc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1328,6 +1328,19 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } } return false; + + case Type_SimdVector: + if (source->kind == Type_SimdVector) { + if (poly->SimdVector.generic_count != nullptr) { + if (!polymorphic_assign_index(&poly->SimdVector.generic_count, &poly->SimdVector.count, source->SimdVector.count)) { + return false; + } + } + if (poly->SimdVector.count == source->SimdVector.count) { + return is_polymorphic_type_assignable(c, poly->SimdVector.elem, source->SimdVector.elem, true, modify_type); + } + } + return false; } return false; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 088853810..354ab6e94 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2803,14 +2803,14 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t goto array_end; } if (is_type_polymorphic(elem)) { - count = 1; + // Ignore } else if (count < 1 || !is_power_of_two(count)) { error(at->count, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); *type = alloc_type_array(elem, count, generic_type); goto array_end; } - *type = alloc_type_simd_vector(count, elem); + *type = alloc_type_simd_vector(count, elem, generic_type); } else { error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); *type = alloc_type_array(elem, count, generic_type); diff --git a/src/types.cpp b/src/types.cpp index 755f78f1c..2d5709b19 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -261,6 +261,7 @@ struct TypeProc { TYPE_KIND(SimdVector, struct { \ i64 count; \ Type *elem; \ + Type *generic_count; \ }) \ TYPE_KIND(RelativePointer, struct { \ Type *pointer_type; \ @@ -1085,10 +1086,11 @@ Type *alloc_type_bit_set() { -Type *alloc_type_simd_vector(i64 count, Type *elem) { +Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr) { Type *t = alloc_type(Type_SimdVector); t->SimdVector.count = count; t->SimdVector.elem = elem; + t->SimdVector.generic_count = generic_count; return t; } @@ -2078,6 +2080,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) { return true; } return is_type_polymorphic(t->Array.elem, or_specialized); + case Type_SimdVector: + if (t->SimdVector.generic_count != nullptr) { + return true; + } + return is_type_polymorphic(t->SimdVector.elem, or_specialized); case Type_DynamicArray: return is_type_polymorphic(t->DynamicArray.elem, or_specialized); case Type_Slice: -- cgit v1.2.3 From f308f37ba112ca361715e470d513749236da026d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 14:51:50 +0100 Subject: Remove need for `simd.splat` --- core/simd/simd.odin | 9 ++---- src/check_expr.cpp | 8 ++++++ src/llvm_backend_const.cpp | 26 +++++++++++++---- src/llvm_backend_expr.cpp | 72 +++++++++++++++++++++++++++++----------------- 4 files changed, 76 insertions(+), 39 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 79d26c845..ef5fdc70b 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -103,10 +103,6 @@ reverse :: intrinsics.simd_reverse rotate_left :: intrinsics.simd_rotate_left rotate_right :: intrinsics.simd_rotate_right -splat :: #force_inline proc "contextless" ($T: typeid/#simd[$LANES]$E, value: E) -> T { - return T{0.. ^[LANES]E { return (^[LANES]E)(v) } @@ -127,12 +123,11 @@ from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T { } bit_not :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_integer(E) { - ones := splat(type_of(v), ~E(0)) - return xor(v, ones) + return xor(v, T(~E(0))) } copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { - neg_zero := to_bits(splat(T, E(-0.0))) + neg_zero := to_bits(T(-0.0)) sign_bit := and(to_bits(sign), neg_zero) magnitude := and(to_bits(v), bit_not(neg_zero)) return transmute(T)or(sign_bit, magnitude) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fcd7818bc..2a3b5bf02 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2713,6 +2713,14 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return check_is_castable_to(c, &x, elem_dst); } + if (is_type_simd_vector(dst)) { + Type *elem = base_array_type(dst); + if (check_is_castable_to(c, operand, elem)) { + return true; + } + } + + return false; } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 3a3067dbc..bd76400de 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -495,9 +495,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = data; return res; } else if (is_type_array(type) && - value.kind != ExactValue_Invalid && - value.kind != ExactValue_String && - value.kind != ExactValue_Compound) { + value.kind != ExactValue_Invalid && + value.kind != ExactValue_String && + value.kind != ExactValue_Compound) { i64 count = type->Array.count; Type *elem = type->Array.elem; @@ -513,8 +513,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count); return res; } else if (is_type_matrix(type) && - value.kind != ExactValue_Invalid && - value.kind != ExactValue_Compound) { + value.kind != ExactValue_Invalid && + value.kind != ExactValue_Compound) { i64 row = type->Matrix.row_count; i64 column = type->Matrix.column_count; GB_ASSERT(row == column); @@ -537,6 +537,22 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)total_elem_count); return res; + } else if (is_type_simd_vector(type) && + value.kind != ExactValue_Invalid && + value.kind != ExactValue_Compound) { + i64 count = type->SimdVector.count; + Type *elem = type->SimdVector.elem; + + lbValue single_elem = lb_const_value(m, elem, value, allow_local); + single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); + + LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + elems[i] = single_elem.value; + } + + res.value = LLVMConstVector(elems, cast(unsigned)count); + return res; } switch (value.kind) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 426becc1c..10c337650 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1820,41 +1820,59 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } - if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { - Type *src_elem = core_array_type(src); - Type *dst_elem = core_array_type(dst); + if (is_type_simd_vector(dst)) { + Type *et = base_array_type(dst); + if (is_type_simd_vector(src)) { + Type *src_elem = core_array_type(src); + Type *dst_elem = core_array_type(dst); - GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); + GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); - lbValue res = {}; - res.type = t; - if (are_types_identical(src_elem, dst_elem)) { - res.value = value.value; - } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { - if (is_type_unsigned(dst_elem)) { - res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); - } else { - res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); - } - } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { - if (is_type_unsigned(src_elem)) { - res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + lbValue res = {}; + res.type = t; + if (are_types_identical(src_elem, dst_elem)) { + res.value = value.value; + } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { + if (is_type_unsigned(dst_elem)) { + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { + if (is_type_unsigned(src_elem)) { + res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + } + } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) { + res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); + } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { + res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); + } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) { + LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), ""); + res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), ""); } else { - res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); } - } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) { - res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); - } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { - res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); - } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) { - LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), ""); - res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), ""); + return res; } else { - GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); + i64 count = get_array_type_count(dst); + LLVMTypeRef vt = lb_type(m, t); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + LLVMValueRef elem = lb_emit_conv(p, value, et).value; + LLVMValueRef vector = LLVMConstNull(vt); + for (i64 i = 0; i < count; i++) { + LLVMValueRef idx = LLVMConstInt(llvm_u32, i, false); + vector = LLVMBuildInsertElement(p->builder, vector, elem, idx, ""); + } + lbValue res = {}; + res.type = t; + res.value = vector; + return res; } - return res; } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { lbValue res = {}; -- cgit v1.2.3 From 208226dba29d46514c8c2b7a8fcd023f1ebf7bd8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 14:55:10 +0100 Subject: Improve `#simd` literal support --- src/check_builtin.cpp | 76 +++++++++++++++++++++++++-------------------------- src/check_expr.cpp | 9 ++++++ 2 files changed, 47 insertions(+), 38 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c432d6080..34b7d14e9 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -425,9 +425,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { Operand x = {}; Operand y = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -467,9 +467,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { Operand x = {}; Operand y = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -521,9 +521,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { Operand x = {}; Operand y = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -601,9 +601,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call Operand x = {}; Operand y = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -655,7 +655,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_extract: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -680,7 +680,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_replace: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -698,8 +698,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } Operand y = {}; - check_expr_with_type_hint(c, &y, ce->args[2], elem); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, elem); + check_expr_with_type_hint(c, &y, ce->args[2], elem); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, elem); if (y.mode == Addressing_Invalid) return false; if (!are_types_identical(y.type, elem)) { gbString et = type_to_string(elem); gbString yt = type_to_string(y.type); @@ -721,7 +721,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_reduce_max: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -745,7 +745,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_reduce_xor: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -769,9 +769,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { Operand x = {}; Operand y = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -843,7 +843,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_select: { Operand cond = {}; - check_expr(c, &cond, ce->args[0]); if (cond.mode == Addressing_Invalid) { return false; } + check_expr(c, &cond, ce->args[0]); if (cond.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(cond.type)) { error(cond.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); @@ -859,9 +859,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call Operand x = {}; Operand y = {}; - check_expr(c, &x, ce->args[1]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[2], x.type); if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[1]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[2], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -900,7 +900,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_nearest: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); @@ -922,7 +922,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_reverse: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -937,13 +937,13 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_rotate_right: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; } Operand offset = {}; - check_expr(c, &offset, ce->args[1]); if (offset.mode == Addressing_Invalid) { return false; } + check_expr(c, &offset, ce->args[1]); if (offset.mode == Addressing_Invalid) return false; convert_to_typed(c, &offset, t_i64); if (!is_type_integer(offset.type) || offset.mode != Addressing_Constant) { error(offset.expr, "'%.*s' expected a constant integer offset"); @@ -961,10 +961,10 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call Operand x = {}; Operand y = {}; Operand z = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } - check_expr_with_type_hint(c, &z, ce->args[2], x.type); if (z.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &z, ce->args[2], x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; convert_to_typed(c, &z, x.type); if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -1010,7 +1010,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_to_bits: { Operand x = {}; - check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); @@ -2933,7 +2933,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (i == j) continue; Operand *b = ops[j]; convert_to_typed(c, a, b->type); - if (a->mode == Addressing_Invalid) { return false; } + if (a->mode == Addressing_Invalid) return false; } } @@ -3616,7 +3616,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; convert_to_typed(c, &x, y.type); if (is_type_untyped(x.type)) { gbString xts = type_to_string(x.type); @@ -4232,7 +4232,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (x.mode == Addressing_Invalid) { return false; } @@ -4289,7 +4289,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; convert_to_typed(c, &x, y.type); if (!are_types_identical(x.type, y.type)) { gbString xts = type_to_string(x.type); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2a3b5bf02..b7568aa70 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -777,6 +777,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type return distance + 6; } } + + if (is_type_simd_vector(dst)) { + Type *dst_elem = base_array_type(dst); + i64 distance = check_distance_between_types(c, operand, dst_elem); + if (distance >= 0) { + return distance + 6; + } + } if (is_type_matrix(dst)) { Type *dst_elem = base_array_type(dst); @@ -786,6 +794,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } } + if (is_type_any(dst)) { if (!is_type_polymorphic(src)) { if (operand->mode == Addressing_Context && operand->type == t_context) { -- cgit v1.2.3 From d0e8a735bae52eaa7d1d852953da721071571020 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 17:09:46 +0100 Subject: Add arithmetic operator support for simd vectors; Add `intrinsics.simd_and_not` --- core/simd/simd.odin | 16 +++++++++++++--- src/check_builtin.cpp | 1 + src/check_expr.cpp | 7 ++----- src/checker_builtin_procs.hpp | 3 +++ src/llvm_backend_expr.cpp | 40 +++++++++++++++++++++++++++++++++++++++- src/llvm_backend_proc.cpp | 4 ++++ src/types.cpp | 3 +++ 7 files changed, 65 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index ef5fdc70b..9673b5cc9 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -43,9 +43,10 @@ shr_masked :: intrinsics.simd_shr_masked add_sat :: intrinsics.simd_add_sat sub_sat :: intrinsics.simd_sub_sat -and :: intrinsics.simd_and -or :: intrinsics.simd_or -xor :: intrinsics.simd_xor +and :: intrinsics.simd_and +or :: intrinsics.simd_or +xor :: intrinsics.simd_xor +and_not :: intrinsics.simd_and_not neg :: intrinsics.simd_neg @@ -132,3 +133,12 @@ copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T magnitude := and(to_bits(v), bit_not(neg_zero)) return transmute(T)or(sign_bit, magnitude) } + +signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + is_nan := ne(v, v) + return select(is_nan, v, copysign(T(1), v)) +} + +recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + return div(T(1), v) +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 34b7d14e9..bfaa91cf8 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -464,6 +464,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_and: case BuiltinProc_simd_or: case BuiltinProc_simd_xor: + case BuiltinProc_simd_and_not: { Operand x = {}; Operand y = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b7568aa70..7fe9b8acf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1590,11 +1590,6 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) { bool check_binary_op(CheckerContext *c, Operand *o, Token op) { Type *main_type = o->type; - if (is_type_simd_vector(main_type)) { - error(op, "Operator '%.*s' is not supported on #simd vector types, please use the intrinsics.simd_*", LIT(op.string)); - return false; - } - // TODO(bill): Handle errors correctly Type *type = base_type(core_array_type(main_type)); Type *ct = core_type(type); @@ -2500,6 +2495,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ gb_string_free(err_str); } + // TODO(bill): Should we support shifts for fixed arrays and #simd vectors? + if (!is_type_integer(x->type)) { gbString err_str = expr_to_string(y->expr); error(node, "Shift operand '%s' must be an integer", err_str); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 350213de2..eb4bc1498 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -135,6 +135,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_and, BuiltinProc_simd_or, BuiltinProc_simd_xor, + BuiltinProc_simd_and_not, BuiltinProc_simd_neg, BuiltinProc_simd_abs, @@ -417,6 +418,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 10c337650..55b76b93a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -258,7 +258,13 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, "")); LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, "")); return lb_addr_load(p, addr); - + } else if (is_type_simd_vector(x.type)) { + Type *elem = base_array_type(x.type); + if (is_type_float(elem)) { + res.value = LLVMBuildFNeg(p->builder, x.value, ""); + } else { + res.value = LLVMBuildNeg(p->builder, x.value, ""); + } } else { GB_PANIC("Unhandled type %s", type_to_string(x.type)); } @@ -2559,6 +2565,38 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri case Token_NotEq: pred = LLVMIntNE; break; } res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, ""); + } else if (is_type_simd_vector(a)) { + LLVMValueRef mask = nullptr; + Type *elem = base_array_type(a); + if (is_type_float(elem)) { + LLVMRealPredicate pred = {}; + switch (op_kind) { + case Token_CmpEq: pred = LLVMRealOEQ; break; + case Token_NotEq: pred = LLVMRealONE; break; + } + mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, ""); + } else { + LLVMIntPredicate pred = {}; + switch (op_kind) { + case Token_CmpEq: pred = LLVMIntEQ; break; + case Token_NotEq: pred = LLVMIntNE; break; + } + mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, ""); + } + GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type))); + + // TODO(bill): is this a good approach to dealing with comparisons of vectors? + char const *name = "llvm.vector.reduce.umax"; + LLVMTypeRef types[1] = {LLVMTypeOf(mask)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = mask; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } else { GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type))); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a56aa862a..99c023311 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1097,11 +1097,15 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_and: case BuiltinProc_simd_or: case BuiltinProc_simd_xor: + case BuiltinProc_simd_and_not: arg1 = lb_build_expr(p, ce->args[1]); switch (builtin_id) { case BuiltinProc_simd_and: op_code = LLVMAnd; break; case BuiltinProc_simd_or: op_code = LLVMOr; break; case BuiltinProc_simd_xor: op_code = LLVMXor; break; + case BuiltinProc_simd_and_not: + res.value = LLVMBuildAnd(p->builder, arg0.value, LLVMBuildNot(p->builder, arg1.value, ""), ""); + return res; } if (op_code) { res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); diff --git a/src/types.cpp b/src/types.cpp index 6f61015d3..ad83e0568 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2306,6 +2306,9 @@ bool is_type_comparable(Type *t) { } } return true; + + case Type_SimdVector: + return true; } return false; } -- cgit v1.2.3 From 5b42dd7707a8921321958934cf5d75ada1d8a006 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 30 May 2022 15:27:09 +0100 Subject: Correct `@(require_results)` on parapoly procedures --- src/check_decl.cpp | 10 ++++++---- src/check_expr.cpp | 8 ++++++++ src/check_stmt.cpp | 10 +++++----- 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d4818892b..86280b6cb 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1014,10 +1014,12 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } - if (pt->result_count == 0 && ac.require_results) { - error(pl->type, "'require_results' is not needed on a procedure with no results"); - } else { - pt->require_results = ac.require_results; + if (ac.require_results) { + if (pt->result_count == 0) { + error(pl->type, "'require_results' is not needed on a procedure with no results"); + } else { + pt->require_results = true; + } } if (ac.link_name.len > 0) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7fe9b8acf..f954f1583 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -442,6 +442,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_ final_proc_type->Proc.is_poly_specialized = true; final_proc_type->Proc.is_polymorphic = true; + final_proc_type->Proc.variadic = src->Proc.variadic; + final_proc_type->Proc.require_results = src->Proc.require_results; + final_proc_type->Proc.c_vararg = src->Proc.c_vararg; + final_proc_type->Proc.has_named_results = src->Proc.has_named_results; + final_proc_type->Proc.diverging = src->Proc.diverging; + final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer; + final_proc_type->Proc.optional_ok = src->Proc.optional_ok; + for (isize i = 0; i < operands.count; i++) { Operand o = operands[i]; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f2c830c1b..b316f940f 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1405,12 +1405,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (kind == Expr_Stmt) { return; } - Ast *expr = strip_or_return_expr(operand.expr); + Ast *expr = strip_or_return_expr(operand.expr); if (expr->kind == Ast_CallExpr) { AstCallExpr *ce = &expr->CallExpr; - Type *t = type_of_expr(ce->proc); - if (is_type_proc(t)) { + Type *t = base_type(type_of_expr(ce->proc)); + if (t->kind == Type_Proc) { if (t->Proc.require_results) { gbString expr_str = expr_to_string(ce->proc); error(node, "'%s' requires that its results must be handled", expr_str); @@ -1421,8 +1421,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } else if (expr->kind == Ast_SelectorCallExpr) { AstSelectorCallExpr *se = &expr->SelectorCallExpr; ast_node(ce, CallExpr, se->call); - Type *t = type_of_expr(ce->proc); - if (is_type_proc(t)) { + Type *t = base_type(type_of_expr(ce->proc)); + if (t->kind == Type_Proc) { if (t->Proc.require_results) { gbString expr_str = expr_to_string(ce->proc); error(node, "'%s' requires that its results must be handled", expr_str); -- cgit v1.2.3 From bb7f291f5fa9d412d5ff15b8de4a46b6ad2704e2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 2 Jun 2022 12:10:43 +0100 Subject: Remove `simd_rem`; Disallow `simd_div` for integers --- core/intrinsics/intrinsics.odin | 3 +-- core/simd/simd.odin | 3 +-- src/check_builtin.cpp | 9 +++++++-- src/check_expr.cpp | 6 ++++++ 4 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 9994a1914..22b5d953d 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -194,8 +194,7 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T --- -simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T --- -simd_rem :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) --- // Keeps Odin's Behaviour // (x << y) if y <= mask else 0 diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 390ff377a..a0a4df28d 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -61,8 +61,7 @@ b64x8 :: #simd[8]b64 add :: intrinsics.simd_add sub :: intrinsics.simd_sub mul :: intrinsics.simd_mul -div :: intrinsics.simd_div -rem :: intrinsics.simd_rem // integers only +div :: intrinsics.simd_div // floats only // Keeps Odin's Behaviour // (x << y) if y <= mask else 0 diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 92e3987a0..8108604ba 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -452,6 +452,13 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return false; } + if (id == BuiltinProc_simd_div && is_type_integer(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' is not supported for integer elements, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + // don't return + } + operand->mode = Addressing_Value; operand->type = x.type; return true; @@ -460,7 +467,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call // Integer only case BuiltinProc_simd_add_sat: case BuiltinProc_simd_sub_sat: - case BuiltinProc_simd_rem: case BuiltinProc_simd_and: case BuiltinProc_simd_or: case BuiltinProc_simd_xor: @@ -492,7 +498,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call switch (id) { case BuiltinProc_simd_add_sat: case BuiltinProc_simd_sub_sat: - case BuiltinProc_simd_rem: if (!is_type_integer(elem)) { gbString xs = type_to_string(x.type); error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f954f1583..58972d2cf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1618,6 +1618,9 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { if (is_type_matrix(main_type)) { error(op, "Operator '%.*s' is only allowed with matrix types", LIT(op.string)); return false; + } else if (is_type_simd_vector(main_type) && is_type_integer(type)) { + error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string)); + return false; } /*fallthrough*/ case Token_Mul: @@ -1669,6 +1672,9 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { if (!is_type_integer(type)) { error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); return false; + } else if (is_type_simd_vector(main_type)) { + error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string)); + return false; } break; -- cgit v1.2.3 From abe122ecb7ef33cff52fd072514de5ad40538bc2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 26 Jun 2022 13:13:07 +0100 Subject: Implement #1859 --- src/check_expr.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 58972d2cf..b42301cd6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3249,8 +3249,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint return; } - - if (!are_types_identical(x->type, y->type)) { + if ((op.kind == Token_CmpAnd || op.kind == Token_CmpOr) && + is_type_boolean(x->type) && is_type_boolean(y->type)) { + // NOTE(bill, 2022-06-26) + // Allow any boolean types within `&&` and `||` + // This is an exception to all other binary expressions since the result + // of a comparison will always be an untyped boolean, and allowing + // any boolean between these two simplifies a lot of expressions + } else if (!are_types_identical(x->type, y->type)) { if (x->type != t_invalid && y->type != t_invalid) { gbString xt = type_to_string(x->type); -- cgit v1.2.3 From 041625381cfd6236de5bc5fea3a398123b7328ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Jul 2022 17:36:03 +0100 Subject: Fix #1888 --- src/check_expr.cpp | 8 ++++++-- src/llvm_backend.hpp | 2 ++ src/llvm_backend_expr.cpp | 38 ++++++++++++++++++++++++++++---------- src/llvm_backend_proc.cpp | 5 +---- src/parser.cpp | 1 + src/parser.hpp | 4 +++- 6 files changed, 41 insertions(+), 17 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b42301cd6..13d6badbe 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8643,6 +8643,8 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type Ast *first_arg = x.expr->SelectorExpr.expr; GB_ASSERT(first_arg != nullptr); + first_arg->state_flags |= StateFlag_SelectorCallExpr; + Type *pt = base_type(x.type); GB_ASSERT(pt->kind == Type_Proc); Type *first_type = nullptr; @@ -8668,6 +8670,7 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type y.mode = first_arg->tav.mode; y.type = first_arg->tav.type; y.value = first_arg->tav.value; + if (check_is_assignable_to(c, &y, first_type)) { // Do nothing, it's valid } else { @@ -10034,9 +10037,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, ce->proc, shorthand); str = gb_string_appendc(str, "("); - for_array(i, ce->args) { + isize idx0 = cast(isize)ce->was_selector; + for (isize i = idx0; i < ce->args.count; i++) { Ast *arg = ce->args[i]; - if (i > 0) { + if (i > idx0) { str = gb_string_appendc(str, ", "); } str = write_expr_to_string(str, arg, shorthand); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index f65e1079e..87a354a49 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -292,6 +292,8 @@ struct lbProcedure { LLVMMetadataRef debug_info; lbCopyElisionHint copy_elision_hint; + + PtrMap selector_values; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 1894e85f6..a95d884b0 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2993,9 +2993,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { return lb_build_addr_ptr(p, ue->expr); } +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr); lbValue lb_build_expr(lbProcedure *p, Ast *expr) { - lbModule *m = p->module; - u16 prev_state_flags = p->state_flags; defer (p->state_flags = prev_state_flags); @@ -3022,6 +3021,32 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { p->state_flags = out; } + + // IMPORTANT NOTE(bill): + // Selector Call Expressions (foo->bar(...)) + // must only evaluate `foo` once as it gets transformed into + // `foo.bar(foo, ...)` + // And if `foo` is a procedure call or something more complex, storing the value + // once is a very good idea + // If a stored value is found, it must be removed from the cache + if (expr->state_flags & StateFlag_SelectorCallExpr) { + lbValue *pp = map_get(&p->selector_values, expr); + if (pp != nullptr) { + lbValue res = *pp; + map_remove(&p->selector_values, expr); + return res; + } + } + lbValue res = lb_build_expr_internal(p, expr); + if (expr->state_flags & StateFlag_SelectorCallExpr) { + map_set(&p->selector_values, expr, res); + } + return res; +} + +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { + lbModule *m = p->module; + expr = unparen_expr(expr); TokenPos expr_pos = ast_token(expr).pos; @@ -3119,14 +3144,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(se, SelectorCallExpr, expr); GB_ASSERT(se->modified_call); - TypeAndValue tav = type_and_value_of_expr(expr); - GB_ASSERT(tav.mode != Addressing_Invalid); - lbValue res = lb_build_call_expr(p, se->call); - - ast_node(ce, CallExpr, se->call); - ce->sce_temp_data = gb_alloc_copy(permanent_allocator(), &res, gb_size_of(res)); - - return res; + return lb_build_call_expr(p, se->call); case_end; case_ast_node(te, TernaryIfExpr, expr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 75ca77641..1c0ecabbe 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -113,6 +113,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->branch_blocks.allocator = a; p->context_stack.allocator = a; p->scope_stack.allocator = a; + map_init(&p->selector_values, a); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); @@ -2832,10 +2833,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); ast_node(ce, CallExpr, expr); - if (ce->sce_temp_data) { - return *(lbValue *)ce->sce_temp_data; - } - lbValue res = lb_build_call_expr_internal(p, expr); if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures diff --git a/src/parser.cpp b/src/parser.cpp index a6f30cdfd..b63c530d6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -164,6 +164,7 @@ Ast *clone_ast(Ast *node) { case Ast_SelectorCallExpr: n->SelectorCallExpr.expr = clone_ast(n->SelectorCallExpr.expr); n->SelectorCallExpr.call = clone_ast(n->SelectorCallExpr.call); + GB_ASSERT(n->SelectorCallExpr.modified_call); break; case Ast_IndexExpr: n->IndexExpr.expr = clone_ast(n->IndexExpr.expr); diff --git a/src/parser.hpp b/src/parser.hpp index c167ef6d5..8719b5e56 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,6 +282,8 @@ enum StateFlag : u8 { StateFlag_type_assert = 1<<2, StateFlag_no_type_assert = 1<<3, + StateFlag_SelectorCallExpr = 1<<6, + StateFlag_BeenHandled = 1<<7, }; @@ -411,7 +413,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token ellipsis; \ ProcInlining inlining; \ bool optional_ok_one; \ - void *sce_temp_data; \ + bool was_selector; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3 From eafa5098aa5fd10554f3348acba5ac6164a4b83e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Jul 2022 18:03:43 +0100 Subject: Fix #1883 --- src/check_expr.cpp | 35 +++++++++++++++++++++-------------- src/llvm_backend_const.cpp | 23 +++++++++++------------ 2 files changed, 32 insertions(+), 26 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 13d6badbe..721b5a5af 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8404,23 +8404,30 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * if (is_type_bit_set(type)) { // NOTE(bill): Encode as an integer - i64 lower = base_type(type)->BitSet.lower; + Type *bt = base_type(type); + BigInt bits = {}; + BigInt one = {}; + big_int_from_u64(&one, 1); - u64 bits = 0; - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - GB_ASSERT(elem->kind != Ast_FieldValue); - TypeAndValue tav = elem->tav; - ExactValue i = exact_value_to_integer(tav.value); - if (i.kind != ExactValue_Integer) { + for_array(i, cl->elems) { + Ast *e = cl->elems[i]; + GB_ASSERT(e->kind != Ast_FieldValue); + + TypeAndValue tav = e->tav; + if (tav.mode != Addressing_Constant) { continue; } - i64 val = big_int_to_i64(&i.value_integer); - val -= lower; - u64 bit = u64(1ll<value = exact_value_u64(bits); + GB_ASSERT(tav.value.kind == ExactValue_Integer); + i64 v = big_int_to_i64(&tav.value.value_integer); + i64 lower = bt->BitSet.lower; + u64 index = cast(u64)(v-lower); + BigInt bit = {}; + big_int_from_u64(&bit, index); + big_int_shl(&bit, &one, &bit); + big_int_or(&bits, &bits, &bit); + } + o->value.kind = ExactValue_Integer; + o->value.value_integer = bits; } else if (is_type_constant_type(type) && cl->elems.count == 0) { ExactValue value = exact_value_compound(node); Type *bt = core_type(type); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 00452eb50..24b2bc3a2 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1025,7 +1025,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } - u64 bits = 0; + BigInt bits = {}; + BigInt one = {}; + big_int_from_u64(&one, 1); + for_array(i, cl->elems) { Ast *e = cl->elems[i]; GB_ASSERT(e->kind != Ast_FieldValue); @@ -1037,18 +1040,14 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc GB_ASSERT(tav.value.kind == ExactValue_Integer); i64 v = big_int_to_i64(&tav.value.value_integer); i64 lower = type->BitSet.lower; - bits |= 1ull< Date: Sun, 24 Jul 2022 12:59:34 +0100 Subject: Fix #1493 --- src/check_expr.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 721b5a5af..ff1745535 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2959,7 +2959,14 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand goto matrix_error; } x->mode = Addressing_Value; - x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count); + if (are_types_identical(xt, yt)) { + if (!is_type_named(x->type) && is_type_named(y->type)) { + // prefer the named type + x->type = y->type; + } + } else { + x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count); + } goto matrix_success; } else if (yt->kind == Type_Array) { if (!are_types_identical(xt->Matrix.elem, yt->Array.elem)) { @@ -3021,7 +3028,6 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand matrix_success: x->type = check_matrix_type_hint(x->type, type_hint); - return; -- cgit v1.2.3 From 0ebe9ba4871d614fac6feb9d66ffe9c3892c1517 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Jul 2022 22:51:34 +0100 Subject: Fix #1901 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ff1745535..cf9f2f751 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9302,7 +9302,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type check_unary_expr(c, o, ue->op, node); } o->expr = node; - return kind; + return Expr_Expr; case_end; -- cgit v1.2.3