From a9182cfd8cf9dd8cec125221e39c0fcd7a3fd3e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 26 Feb 2023 13:26:35 +0000 Subject: Allow compound literals to access fields through `using` --- src/check_expr.cpp | 205 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 120 insertions(+), 85 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 00d394966..b9588a798 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7890,6 +7890,124 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no return Expr_Expr; } + +gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice const &elems, Operand *o, Type *type, bool &is_constant) { + Type *bt = base_type(type); + + StringSet fields_visited = {}; + defer (string_set_destroy(&fields_visited)); + + StringMap fields_visited_through_raw_union = {}; + defer (string_map_destroy(&fields_visited_through_raw_union)); + + for (Ast *elem : elems) { + 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 (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(fv->field, "Unknown field '%.*s' in structure literal", LIT(name)); + continue; + } + + Entity *field = bt->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); + if (string_set_update(&fields_visited, name)) { + if (sel.index.count > 1) { + if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { + error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + } else { + error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string)); + } + } else { + error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string)); + } + continue; + } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { + error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + continue; + } + if (sel.indirect) { + error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name)); + continue; + } + + if (sel.index.count > 1) { + if (is_constant) { + Type *ft = type; + for (i32 index : sel.index) { + Type *bt = base_type(ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + is_constant = false; + break; + } + ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + ft = bt->Array.elem; + break; + default: + GB_PANIC("invalid type: %s", type_to_string(ft)); + break; + } + } + if (is_constant && + (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) { + is_constant = false; + } + } + + Type *nested_ft = bt; + for (i32 index : sel.index) { + Type *bt = base_type(nested_ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + for (Entity *re : bt->Struct.fields) { + string_map_set(&fields_visited_through_raw_union, re->token.string, sel.entity->token.string); + } + } + nested_ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + nested_ft = bt->Array.elem; + break; + default: + GB_PANIC("invalid type %s", type_to_string(nested_ft)); + break; + } + } + field = sel.entity; + } + + + 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); + } + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); @@ -7977,45 +8095,13 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * 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; - } - - 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)); - break; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - break; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - - check_assignment(c, &o, field->type, str_lit("structure literal")); + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); } - } } break; } - 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--) { @@ -8029,58 +8115,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (cl->elems[0]->kind == Ast_FieldValue) { - TEMPORARY_ALLOCATOR_GUARD(); - - bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); - - for (Ast *elem : cl->elems) { - 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 (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; - } - - fields_visited[sel.index[0]] = true; - - 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); - } - - check_assignment(c, &o, field->type, str_lit("structure literal")); - } + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); } else { bool seen_field_value = false; -- cgit v1.2.3