mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-07 11:04:17 +00:00
Allow compound literals to access fields through using
This commit is contained in:
@@ -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<Ast *> const &elems, Operand *o, Type *type, bool &is_constant) {
|
||||
Type *bt = base_type(type);
|
||||
|
||||
StringSet fields_visited = {};
|
||||
defer (string_set_destroy(&fields_visited));
|
||||
|
||||
StringMap<String> 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;
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@ struct TypeIsPointer<T *> {
|
||||
enum {value = true};
|
||||
};
|
||||
|
||||
template <typename T> struct TypeIsPtrSizedInteger { enum {value = false}; };
|
||||
template <> struct TypeIsPtrSizedInteger<isize> { enum {value = true}; };
|
||||
template <> struct TypeIsPtrSizedInteger<usize> { enum {value = true}; };
|
||||
|
||||
|
||||
#include "unicode.cpp"
|
||||
#include "array.cpp"
|
||||
#include "threading.cpp"
|
||||
|
||||
@@ -423,7 +423,7 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *
|
||||
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
show_error_on_line(pos, end);
|
||||
// show_error_on_line(pos, end);
|
||||
} else if (pos.line == 0) {
|
||||
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
error_out_va(fmt, va);
|
||||
@@ -451,7 +451,7 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
show_error_on_line(pos, end);
|
||||
// show_error_on_line(pos, end);
|
||||
} else if (pos.line == 0) {
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
|
||||
@@ -386,6 +386,31 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
|
||||
return value;
|
||||
}
|
||||
|
||||
gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, Ast *elem) {
|
||||
GB_ASSERT(!sel.indirect);
|
||||
for (i32 index : sel.index) {
|
||||
Type *bt = base_type(ft);
|
||||
switch (bt->kind) {
|
||||
case Type_Struct:
|
||||
if (bt->Struct.is_raw_union) {
|
||||
return false;
|
||||
}
|
||||
ft = bt->Struct.fields[index]->type;
|
||||
break;
|
||||
case Type_Array:
|
||||
ft = bt->Array.elem;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_type_raw_union(ft) || is_type_typeid(ft)) {
|
||||
return false;
|
||||
}
|
||||
return lb_is_elem_const(elem, ft);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
@@ -979,12 +1004,58 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
|
||||
Selection sel = lookup_field(type, name, false);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
|
||||
Entity *f = type->Struct.fields[sel.index[0]];
|
||||
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
visited[index] = true;
|
||||
if (sel.index.count == 1) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
visited[index] = true;
|
||||
} else {
|
||||
if (!visited[index]) {
|
||||
values[index] = lb_const_value(m, f->type, {}, false).value;
|
||||
visited[index] = true;
|
||||
}
|
||||
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
|
||||
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
|
||||
|
||||
if (lb_is_nested_possibly_constant(type, sel, fv->value)) {
|
||||
bool is_constant = true;
|
||||
Type *cv_type = f->type;
|
||||
for (isize j = 1; j < sel.index.count; j++) {
|
||||
i32 index = sel.index[j];
|
||||
Type *cvt = base_type(cv_type);
|
||||
|
||||
if (cvt->kind == Type_Struct) {
|
||||
if (cvt->Struct.is_raw_union) {
|
||||
// sanity check which should have been caught by `lb_is_nested_possibly_constant`
|
||||
is_constant = false;
|
||||
break;
|
||||
}
|
||||
cv_type = cvt->Struct.fields[index]->type;
|
||||
|
||||
if (is_type_struct(cv_type)) {
|
||||
auto cv_field_remapping = lb_get_struct_remapping(m, cv_type);
|
||||
idx_list[j-1] = cast(unsigned)cv_field_remapping[index];
|
||||
} else {
|
||||
idx_list[j-1] = cast(unsigned)index;
|
||||
}
|
||||
} else if (cvt->kind == Type_Array) {
|
||||
cv_type = cvt->Array.elem;
|
||||
|
||||
idx_list[j-1] = cast(unsigned)index;
|
||||
} else {
|
||||
GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type));
|
||||
}
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
|
||||
GB_ASSERT(LLVMIsConstant(elem_value));
|
||||
values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -4044,7 +4044,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
|
||||
@@ -4093,12 +4092,25 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
ast_node(fv, FieldValue, elem);
|
||||
String name = fv->field->Ident.token.string;
|
||||
Selection sel = lookup_field(bt, name, false);
|
||||
index = sel.index[0];
|
||||
GB_ASSERT(!sel.indirect);
|
||||
|
||||
elem = fv->value;
|
||||
TypeAndValue tav = type_and_value_of_expr(elem);
|
||||
if (sel.index.count > 1) {
|
||||
if (lb_is_nested_possibly_constant(type, sel, elem)) {
|
||||
continue;
|
||||
}
|
||||
lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel);
|
||||
field_expr = lb_build_expr(p, elem);
|
||||
field_expr = lb_emit_conv(p, field_expr, sel.entity->type);
|
||||
lb_emit_store(p, dst, field_expr);
|
||||
continue;
|
||||
}
|
||||
|
||||
index = sel.index[0];
|
||||
} else {
|
||||
TypeAndValue tav = type_and_value_of_expr(elem);
|
||||
Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index);
|
||||
GB_ASSERT(sel.index.count == 1);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
index = sel.index[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -915,7 +915,7 @@ gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t)
|
||||
if (field_remapping == nullptr) {
|
||||
field_remapping = map_get(&m->struct_field_remapping, cast(void *)t);
|
||||
}
|
||||
GB_ASSERT(field_remapping != nullptr);
|
||||
GB_ASSERT_MSG(field_remapping != nullptr, "%s", type_to_string(t));
|
||||
return *field_remapping;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
template <typename T>
|
||||
struct PtrSet {
|
||||
static_assert(TypeIsPointer<T>::value, "PtrSet::T must be a pointer");
|
||||
static_assert(TypeIsPointer<T>::value || TypeIsPtrSizedInteger<T>::value, "PtrSet::T must be a pointer");
|
||||
static constexpr uintptr TOMBSTONE = ~(uintptr)(0ull);
|
||||
|
||||
T * keys;
|
||||
|
||||
@@ -430,7 +430,6 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user