Browse Source

Allow compound literals to access fields through `using`

gingerBill 2 years ago
parent
commit
a9182cfd8c
8 changed files with 219 additions and 97 deletions
  1. 120 85
      src/check_expr.cpp
  2. 5 0
      src/common.cpp
  3. 2 2
      src/error.cpp
  4. 74 3
      src/llvm_backend_const.cpp
  5. 16 4
      src/llvm_backend_expr.cpp
  6. 1 1
      src/llvm_backend_utility.cpp
  7. 1 1
      src/ptr_set.cpp
  8. 0 1
      src/types.cpp

+ 120 - 85
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<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;
 

+ 5 - 0
src/common.cpp

@@ -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"

+ 2 - 2
src/error.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);

+ 74 - 3
src/llvm_backend_const.cpp

@@ -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 {

+ 16 - 4
src/llvm_backend_expr.cpp

@@ -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];
 				}
 

+ 1 - 1
src/llvm_backend_utility.cpp

@@ -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 - 1
src/ptr_set.cpp

@@ -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;

+ 0 - 1
src/types.cpp

@@ -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")}},