浏览代码

Add experimental atom op tables for llvm-backend

gingerBill 5 年之前
父节点
当前提交
e27f5796d6
共有 11 个文件被更改,包括 552 次插入14 次删除
  1. 21 3
      src/check_decl.cpp
  2. 76 3
      src/check_expr.cpp
  3. 55 0
      src/check_stmt.cpp
  4. 14 1
      src/check_type.cpp
  5. 211 0
      src/checker.cpp
  6. 11 0
      src/checker.hpp
  7. 2 0
      src/entity.cpp
  8. 128 2
      src/llvm_backend.cpp
  9. 7 1
      src/llvm_backend.hpp
  10. 2 0
      src/parser.hpp
  11. 25 4
      src/types.cpp

+ 21 - 3
src/check_decl.cpp

@@ -256,9 +256,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 	GB_ASSERT(e->type == nullptr);
 	GB_ASSERT(e->type == nullptr);
 
 
 	DeclInfo *decl = decl_info_of_entity(e);
 	DeclInfo *decl = decl_info_of_entity(e);
-	if (decl != nullptr) {
-		check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr);
-	}
 
 
 	bool is_distinct = is_type_distinct(init_expr);
 	bool is_distinct = is_type_distinct(init_expr);
 	Ast *te = remove_type_alias_clutter(init_expr);
 	Ast *te = remove_type_alias_clutter(init_expr);
@@ -280,6 +277,10 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 		error(init_expr, "'distinct' cannot be applied to 'typeid'");
 		error(init_expr, "'distinct' cannot be applied to 'typeid'");
 		is_distinct = false;
 		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_distinct) {
 		e->type = bt;
 		e->type = bt;
 		named->Named.base = bt;
 		named->Named.base = bt;
@@ -298,6 +299,23 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 		}
 		}
 	}
 	}
 
 
+	if (decl != nullptr) {
+		AttributeContext ac = {};
+		check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+		if (ac.atom_op_table != nullptr) {
+			Type *bt = base_type(e->type);
+			switch (bt->kind) {
+			case Type_Struct:
+				bt->Struct.atom_op_table = ac.atom_op_table;
+				break;
+			default:
+				error(e->token, "Only struct types can have custom atom operations");
+				gb_free(heap_allocator(), ac.atom_op_table);
+				break;
+			}
+		}
+	}
+
 
 
 	// using decl
 	// using decl
 	if (decl->is_using) {
 	if (decl->is_using) {

+ 76 - 3
src/check_expr.cpp

@@ -53,7 +53,7 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
 void     check_expr                     (CheckerContext *c, Operand *operand, Ast *expression);
 void     check_expr                     (CheckerContext *c, Operand *operand, Ast *expression);
 void     check_multi_expr               (CheckerContext *c, Operand *operand, Ast *expression);
 void     check_multi_expr               (CheckerContext *c, Operand *operand, Ast *expression);
 void     check_multi_expr_or_type       (CheckerContext *c, Operand *operand, Ast *expression);
 void     check_multi_expr_or_type       (CheckerContext *c, Operand *operand, Ast *expression);
-void     check_expr_or_type             (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint = nullptr);
+void     check_expr_or_type             (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
 ExprKind check_expr_base                (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
 ExprKind check_expr_base                (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
 void     check_expr_with_type_hint      (CheckerContext *c, Operand *o, Ast *e, Type *t);
 void     check_expr_with_type_hint      (CheckerContext *c, Operand *o, Ast *e, Type *t);
 Type *   check_type                     (CheckerContext *c, Ast *expression);
 Type *   check_type                     (CheckerContext *c, Ast *expression);
@@ -8944,7 +8944,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 	case_end;
 	case_end;
 
 
 	case_ast_node(ue, UnaryExpr, node);
 	case_ast_node(ue, UnaryExpr, node);
+		Ast *prev_unary_address_hint = c->unary_address_hint;
+		c->unary_address_hint = unparen_expr(node);
 		check_expr_base(c, o, ue->expr, type_hint);
 		check_expr_base(c, o, ue->expr, type_hint);
+		c->unary_address_hint = prev_unary_address_hint;
 		node->viral_state_flags |= ue->expr->viral_state_flags;
 		node->viral_state_flags |= ue->expr->viral_state_flags;
 
 
 		if (o->mode == Addressing_Invalid) {
 		if (o->mode == Addressing_Invalid) {
@@ -9070,6 +9073,47 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			return Expr_Expr;
 			return Expr_Expr;
 		}
 		}
 
 
+		if (t->kind == Type_Struct) {
+			TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
+			if (atom_op_table != nullptr) {
+				if (atom_op_table->op[TypeAtomOp_index_set]) {
+					if (c->assignment_lhs_hint == node) {
+						o->mode = Addressing_AtomOpAssign;
+						o->type = o->type;
+						o->expr = node;
+						return kind;
+					}
+				}
+				if (atom_op_table->op[TypeAtomOp_index_get]) {
+					Entity *e = atom_op_table->op[TypeAtomOp_index_get];
+					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;
+					}	
+
+					GB_ASSERT(e->identifier != nullptr);
+					Ast *proc_ident = clone_ast(e->identifier);
+
+					auto args = array_make<Ast *>(heap_allocator(), 2);
+					args[0] = ie->expr;
+					args[1] = ie->index;
+
+					GB_ASSERT(c->file != nullptr);
+					Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {});
+					check_expr_base(c, o, fake_call, type_hint);
+					AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call};
+					map_set(&c->info->atom_op_map, hash_pointer(node), entry);
+					o->expr = node;
+					return kind;
+				}
+			}
+		}
+
+
 		i64 max_count = -1;
 		i64 max_count = -1;
 		bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
 		bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
 
 
@@ -9133,8 +9177,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 				o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr);
 				o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr);
 			}
 			}
 		}
 		}
-
-		node->viral_state_flags |= ie->index->viral_state_flags;
 	case_end;
 	case_end;
 
 
 
 
@@ -9191,6 +9233,37 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			if (is_type_soa_struct(t)) {
 			if (is_type_soa_struct(t)) {
 				valid = true;
 				valid = true;
 				o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
 				o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
+			} else {
+				TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
+				if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) {
+					Entity *e = atom_op_table->op[TypeAtomOp_slice];
+					GB_ASSERT(e->identifier != nullptr);
+					Ast *proc_ident = clone_ast(e->identifier);
+
+					Ast *expr = se->expr;
+					if (o->mode == Addressing_Variable) {
+						expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr);
+					} else if (is_type_pointer(o->type)) {
+						// Okay
+					} else {
+						gbString str = expr_to_string(node);
+						error(node, "Cannot slice '%s', value is not addressable", str);
+						gb_string_free(str);
+						o->mode = Addressing_Invalid;
+						o->expr = node;
+						return kind;
+					}
+					auto args = array_make<Ast *>(heap_allocator(), 1);
+					args[0] = expr;
+
+
+					GB_ASSERT(c->file != nullptr);
+					Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {});
+					check_expr_base(c, o, fake_call, type_hint);
+					AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call};
+					map_set(&c->info->atom_op_map, hash_pointer(node), entry);
+					valid = true;
+				}
 			}
 			}
 			break;
 			break;
 		}
 		}

+ 55 - 0
src/check_stmt.cpp

@@ -1296,9 +1296,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 					o->expr = as->lhs[i];
 					o->expr = as->lhs[i];
 					o->mode = Addressing_Value;
 					o->mode = Addressing_Value;
 				} else {
 				} else {
+					ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]);
 					check_expr(ctx, &lhs_operands[i], as->lhs[i]);
 					check_expr(ctx, &lhs_operands[i], as->lhs[i]);
 				}
 				}
 			}
 			}
+			ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint
 
 
 			check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs);
 			check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs);
 
 
@@ -1310,8 +1312,61 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 				}
 				}
 			}
 			}
 
 
+			auto lhs_to_ignore = array_make<bool>(ctx->allocator, lhs_count);
+			defer (array_free(&lhs_to_ignore));
+
 			isize max = gb_min(lhs_count, rhs_count);
 			isize max = gb_min(lhs_count, rhs_count);
+			// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
+			// correctly for assignments
 			for (isize i = 0; i < max; i++) {
 			for (isize i = 0; i < max; i++) {
+				if (lhs_operands[i].mode == Addressing_AtomOpAssign) {
+					Operand lhs = lhs_operands[i];
+
+					Type *t = base_type(lhs.type);
+					GB_ASSERT(t->kind == Type_Struct);
+					ast_node(ie, IndexExpr, unparen_expr(lhs.expr));
+
+					TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
+					GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr);
+					Entity *e = atom_op_table->op[TypeAtomOp_index_set];
+
+					GB_ASSERT(e->identifier != nullptr);
+					Ast *proc_ident = clone_ast(e->identifier);
+					GB_ASSERT(ctx->file != nullptr);
+
+
+					TypeAndValue tv = type_and_value_of_expr(ie->expr);
+					Ast *expr = ie->expr;
+					if (is_type_pointer(tv.type)) {
+						// Okay
+					} else if (tv.mode == Addressing_Variable) {
+						// NOTE(bill): Hack it to take the address instead
+						expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr);
+					} else {
+						continue;
+					}
+
+					auto args = array_make<Ast *>(heap_allocator(), 3);
+					args[0] = expr;
+					args[1] = ie->index;
+					args[2] = rhs_operands[i].expr;
+
+					Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {});
+					Operand fake_operand = {};
+					fake_operand.expr = lhs.expr;
+					check_expr_base(ctx, &fake_operand, fake_call, nullptr);
+					AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call};
+					map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry);
+
+					lhs_to_ignore[i] = true;
+
+				}
+			}
+
+			for (isize i = 0; i < max; i++) {
+				if (lhs_to_ignore[i]) {
+					continue;
+				}
 				check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
 				check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
 			}
 			}
 			if (lhs_count != rhs_count) {
 			if (lhs_count != rhs_count) {

+ 14 - 1
src/check_type.cpp

@@ -329,12 +329,25 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
 
 
 	auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type));
 	auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type));
 	if (found_gen_types) {
 	if (found_gen_types) {
-		array_add(found_gen_types, e);
+		array_add(found_gen_types, e);	
 	} else {
 	} else {
 		auto array = array_make<Entity *>(heap_allocator());
 		auto array = array_make<Entity *>(heap_allocator());
 		array_add(&array, e);
 		array_add(&array, e);
 		map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
 		map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
 	}
 	}
+
+	{
+		Type *dst_bt = base_type(named_type);
+		Type *src_bt = base_type(original_type);
+		if ((dst_bt != nullptr && src_bt != nullptr) &&
+		    (dst_bt->kind == src_bt->kind)){
+			if (dst_bt->kind == Type_Struct) {
+				if (dst_bt->Struct.atom_op_table == nullptr) {
+					dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table;
+				}
+			}
+		}
+	}
 }
 }
 
 
 void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
 void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {

+ 211 - 0
src/checker.cpp

@@ -2,6 +2,7 @@
 #include "types.cpp"
 #include "types.cpp"
 
 
 void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
 void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
+void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
 
 
 
 
 bool is_operand_value(Operand o) {
 bool is_operand_value(Operand o) {
@@ -810,6 +811,9 @@ void init_checker_info(CheckerInfo *i) {
 	if (i->allow_identifier_uses) {
 	if (i->allow_identifier_uses) {
 		array_init(&i->identifier_uses, a);
 		array_init(&i->identifier_uses, a);
 	}
 	}
+
+	map_init(&i->atom_op_map, a);
+
 }
 }
 
 
 void destroy_checker_info(CheckerInfo *i) {
 void destroy_checker_info(CheckerInfo *i) {
@@ -828,6 +832,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	array_free(&i->required_foreign_imports_through_force);
 	array_free(&i->required_foreign_imports_through_force);
 	array_free(&i->required_global_variables);
 	array_free(&i->required_global_variables);
 
 
+	map_destroy(&i->atom_op_map);
 }
 }
 
 
 CheckerContext make_checker_context(Checker *c) {
 CheckerContext make_checker_context(Checker *c) {
@@ -2413,7 +2418,213 @@ DECL_ATTRIBUTE_PROC(const_decl_attribute) {
 	return false;
 	return false;
 }
 }
 
 
+DECL_ATTRIBUTE_PROC(type_decl_attribute) {
+	if (name == "private") {
+		// NOTE(bill): Handled elsewhere `check_collect_value_decl`
+		return true;
+	} else if (name == "index_get") {
+		if (value != nullptr) {
+			Operand o = {};
+			check_expr_or_type(c, &o, value);
+			Entity *e = entity_of_node(value);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				if (ac->deferred_procedure.entity != nullptr) {
+					error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
+				}
+
+				bool valid = true;
+
+				{
+					Type *pt = base_type(e->type);
+					GB_ASSERT(pt->kind == Type_Proc);
+
+					if (pt->Proc.result_count == 0) {
+						error(value, "'%s' attribute must return something", LIT(name));
+						valid = false;
+					}
+
+					if (pt->Proc.param_count < 2) {
+						error(value, "'%s' attribute must allow for 2 parameters", LIT(name));
+						valid = false;
+					} else {
+						isize minimum_param_count = 0;
+						for_array(i, pt->Proc.params->Tuple.variables) {
+							Entity *param = pt->Proc.params->Tuple.variables[i];
+							if (param->kind == Entity_Variable) {
+								if (param->Variable.param_value.kind == ParameterValue_Invalid) {
+									minimum_param_count += 1;
+								} else {
+									break;
+								}
+							} else if (param->kind == Entity_Constant) {
+								minimum_param_count += 1;
+							} else {
+								break;
+							}
+						}
+
+						if (minimum_param_count > 2) {
+							error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name));
+							value = false;
+						}
+					}
+				}
 
 
+				if (valid && build_context.use_llvm_api) {
+					if (ac->atom_op_table == nullptr) {
+						ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+					}
+					ac->atom_op_table->op[TypeAtomOp_index_get] = e;
+				}
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	} else if (name == "index_set") {
+		if (value != nullptr) {
+			Operand o = {};
+			check_expr_or_type(c, &o, value);
+			Entity *e = entity_of_node(value);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				if (ac->deferred_procedure.entity != nullptr) {
+					error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
+				}
+
+				bool valid = true;
+
+				{
+					Type *pt = base_type(e->type);
+					GB_ASSERT(pt->kind == Type_Proc);
+
+					if (pt->Proc.param_count < 3) {
+						error(value, "'%s' attribute must allow for 3 parameters", LIT(name));
+						valid = false;
+					} else {
+						isize minimum_param_count = 0;
+						for_array(i, pt->Proc.params->Tuple.variables) {
+							Entity *param = pt->Proc.params->Tuple.variables[i];
+							if (param->kind == Entity_Variable) {
+								if (param->Variable.param_value.kind == ParameterValue_Invalid) {
+									minimum_param_count += 1;
+								} else {
+									break;
+								}
+							} else if (param->kind == Entity_Constant) {
+								minimum_param_count += 1;
+							} else {
+								break;
+							}
+						}
+
+						if (minimum_param_count > 3) {
+							error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name));
+							value = false;
+						}
+					}
+
+					if (pt->Proc.variadic || pt->Proc.c_vararg) {
+						error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
+						value = false;
+					}
+				}
+
+				if (valid && build_context.use_llvm_api) {
+					if (ac->atom_op_table == nullptr) {
+						ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+					}
+					ac->atom_op_table->op[TypeAtomOp_index_set] = e;
+				}
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	} else if (name == "slice") {
+		if (value != nullptr) {
+			Operand o = {};
+			check_expr_or_type(c, &o, value);
+			Entity *e = entity_of_node(value);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				if (ac->deferred_procedure.entity != nullptr) {
+					error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
+				}
+
+				bool valid = true;
+
+				{
+					Type *pt = base_type(e->type);
+					GB_ASSERT(pt->kind == Type_Proc);
+
+					if (pt->Proc.param_count < 1) {
+						error(value, "'%s' attribute must allow for 1 parameter", LIT(name));
+						valid = false;
+					} else {
+						isize minimum_param_count = 0;
+						for_array(i, pt->Proc.params->Tuple.variables) {
+							Entity *param = pt->Proc.params->Tuple.variables[i];
+							if (param->kind == Entity_Variable) {
+								if (param->Variable.param_value.kind == ParameterValue_Invalid) {
+									minimum_param_count += 1;
+								} else {
+									break;
+								}
+							} else if (param->kind == Entity_Constant) {
+								minimum_param_count += 1;
+							} else {
+								break;
+							}
+						}
+
+						if (minimum_param_count > 1) {
+							error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name));
+							value = false;
+						}
+						{
+							Entity *param = pt->Proc.params->Tuple.variables[0];
+							Type *param_type = base_type(param->type);
+							if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) {
+								// okay
+							} else {
+								error(value, "'%s' attribute's first parameter must be a pointer", LIT(name));
+								value = false;
+							}
+
+						}
+					}
+
+					if (pt->Proc.variadic || pt->Proc.c_vararg) {
+						error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
+						value = false;
+					}
+
+					if (pt->Proc.result_count != 1) {
+						error(value, "'%s' attribute must return 1 result", LIT(name));
+						value = false;
+					} else {
+						Type *rt = pt->Proc.results->Tuple.variables[0]->type;
+						rt = base_type(rt);
+						if (!is_type_slice(rt)) {
+							error(value, "'%s' attribute must return a slice", LIT(name));
+							value = false;
+						}
+					}
+				}
+
+				if (valid && build_context.use_llvm_api) {
+					if (ac->atom_op_table == nullptr) {
+						ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
+					}
+					ac->atom_op_table->op[TypeAtomOp_slice] = e;
+				}
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	}
+	return false;
+}
 
 
 
 
 #include "check_expr.cpp"
 #include "check_expr.cpp"

+ 11 - 0
src/checker.hpp

@@ -107,6 +107,7 @@ struct AttributeContext {
 	String  thread_local_model;
 	String  thread_local_model;
 	String  deprecated_message;
 	String  deprecated_message;
 	DeferredProcedure deferred_procedure;
 	DeferredProcedure deferred_procedure;
+	struct TypeAtomOpTable *atom_op_table;
 };
 };
 
 
 AttributeContext make_attribute_context(String link_prefix) {
 AttributeContext make_attribute_context(String link_prefix) {
@@ -232,6 +233,11 @@ struct ForeignContext {
 typedef Array<Entity *> CheckerTypePath;
 typedef Array<Entity *> CheckerTypePath;
 typedef Array<Type *>   CheckerPolyPath;
 typedef Array<Type *>   CheckerPolyPath;
 
 
+struct AtomOpMapEntry {
+	u32  kind;
+	Ast *node;
+};
+
 
 
 // CheckerInfo stores all the symbol information for a type-checked program
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
 struct CheckerInfo {
@@ -262,6 +268,8 @@ struct CheckerInfo {
 	Array<Entity *>       required_foreign_imports_through_force;
 	Array<Entity *>       required_foreign_imports_through_force;
 	Array<Entity *>       required_global_variables;
 	Array<Entity *>       required_global_variables;
 
 
+	Map<AtomOpMapEntry>   atom_op_map; // Key: Ast *
+
 
 
 	bool allow_identifier_uses;
 	bool allow_identifier_uses;
 	Array<Ast *> identifier_uses; // only used by 'odin query'
 	Array<Ast *> identifier_uses; // only used by 'odin query'
@@ -301,6 +309,9 @@ struct CheckerContext {
 	bool       hide_polymorphic_errors;
 	bool       hide_polymorphic_errors;
 	bool       in_polymorphic_specialization;
 	bool       in_polymorphic_specialization;
 	Scope *    polymorphic_scope;
 	Scope *    polymorphic_scope;
+
+	Ast *assignment_lhs_hint;
+	Ast *unary_address_hint;
 };
 };
 
 
 struct Checker {
 struct Checker {

+ 2 - 0
src/entity.cpp

@@ -114,6 +114,8 @@ struct Entity {
 	isize       order_in_src;
 	isize       order_in_src;
 	String      deprecated_message;
 	String      deprecated_message;
 
 
+	// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
+	// later entity kinds
 	union {
 	union {
 		struct {
 		struct {
 			ExactValue value;
 			ExactValue value;

+ 128 - 2
src/llvm_backend.cpp

@@ -160,8 +160,83 @@ void lb_addr_store(lbProcedure *p, lbAddr const &addr, lbValue value) {
 		value.value = LLVMConstNull(lb_type(p->module, t));
 		value.value = LLVMConstNull(lb_type(p->module, t));
 	}
 	}
 
 
+	if (addr.kind == lbAddr_AtomOp_index_set) {
+		lbValue ptr = addr.addr;
+		lbValue index = addr.index_set.index;
+		Ast *node = addr.index_set.node;
 
 
-	if (addr.kind == lbAddr_Map) {
+		ast_node(ce, CallExpr, node);
+		Type *proc_type = type_and_value_of_expr(ce->proc).type;
+		proc_type = base_type(proc_type);
+		GB_ASSERT(is_type_proc(proc_type));
+		TypeProc *pt = &proc_type->Proc;
+
+		isize arg_count = 3;
+		isize param_count = 0;
+		if (pt->params) {
+			GB_ASSERT(pt->params->kind == Type_Tuple);
+			param_count = pt->params->Tuple.variables.count;
+		}
+
+
+		auto args = array_make<lbValue>(heap_allocator(), gb_max(arg_count, param_count));
+		args[0] = ptr;
+		args[1] = index;
+		args[2] = value;
+
+		isize arg_index = arg_count;
+		if (arg_count < param_count) {
+			lbModule *m = p->module;
+			String proc_name = {};
+			if (p->entity != nullptr) {
+				proc_name = p->entity->token.string;
+			}
+			TokenPos pos = ast_token(ce->proc).pos;
+
+			TypeTuple *param_tuple = &pt->params->Tuple;
+
+			isize end = cast(isize)param_count;
+			while (arg_index < end) {
+				Entity *e = param_tuple->variables[arg_index];
+				GB_ASSERT(e->kind == Entity_Variable);
+
+				switch (e->Variable.param_value.kind) {
+				case ParameterValue_Constant:
+					args[arg_index++] = lb_const_value(p->module, e->type, e->Variable.param_value.value);
+					break;
+				case ParameterValue_Nil:
+					args[arg_index++] = lb_const_nil(m, e->type);
+					break;
+				case ParameterValue_Location:
+					args[arg_index++] = lb_emit_source_code_location(p, proc_name, pos);
+					break;
+				case ParameterValue_Value:
+					args[arg_index++] = lb_build_expr(p, e->Variable.param_value.ast_value);
+					break;
+				}
+			}
+		}
+
+		Entity *e = entity_from_expr(ce->proc);
+		GB_ASSERT(e != nullptr);
+		GB_ASSERT(is_type_polymorphic(e->type));
+
+		{
+			lbValue *found = nullptr;
+			if (p->module != e->code_gen_module) {
+				gb_mutex_lock(&p->module->mutex);
+			}
+			found = map_get(&e->code_gen_module->values, hash_entity(e));
+			if (p->module != e->code_gen_module) {
+				gb_mutex_unlock(&p->module->mutex);
+			}
+			GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(e->token.string));
+
+			lb_emit_call(p, *found, args);
+		}
+
+		return;
+	} else if (addr.kind == lbAddr_Map) {
 		lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value);
 		lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value);
 		return;
 		return;
 	} else if (addr.kind == lbAddr_BitField) {
 	} else if (addr.kind == lbAddr_BitField) {
@@ -8587,7 +8662,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 	expr = unparen_expr(expr);
 	expr = unparen_expr(expr);
 
 
 	TypeAndValue tv = type_and_value_of_expr(expr);
 	TypeAndValue tv = type_and_value_of_expr(expr);
-	GB_ASSERT(tv.mode != Addressing_Invalid);
+	GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "%s", expr_to_string(expr));
 	GB_ASSERT(tv.mode != Addressing_Type);
 	GB_ASSERT(tv.mode != Addressing_Type);
 
 
 	if (tv.value.kind != ExactValue_Invalid) {
 	if (tv.value.kind != ExactValue_Invalid) {
@@ -9301,6 +9376,27 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 			return lb_addr(val);
 			return lb_addr(val);
 		}
 		}
 
 
+		if (!is_type_indexable(t)) {
+			AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr));
+			if (found != nullptr) {
+				if (found->kind == TypeAtomOp_index_get) {
+					return lb_build_addr(p, found->node);
+				} else if (found->kind == TypeAtomOp_index_get_ptr) {
+					return lb_addr(lb_build_expr(p, found->node));
+				} else if (found->kind == TypeAtomOp_index_set) {
+					lbValue ptr = lb_build_addr_ptr(p, ie->expr);
+					if (deref) {
+						ptr = lb_emit_load(p, ptr);
+					}
+
+					lbAddr addr = {lbAddr_AtomOp_index_set};
+					addr.addr = ptr;
+					addr.index_set.index = lb_build_expr(p, ie->index);
+					addr.index_set.node = found->node;
+					return addr;
+				}
+			}
+		}
 		GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
 		GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
 
 
 		if (is_type_map(t)) {
 		if (is_type_map(t)) {
@@ -9450,6 +9546,36 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 
 
 		bool no_indices = se->low == nullptr && se->high == nullptr;
 		bool no_indices = se->low == nullptr && se->high == nullptr;
 
 
+		{
+			Type *type = base_type(type_of_expr(se->expr));
+			if (type->kind == Type_Struct && !is_type_soa_struct(type)) {
+				TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table;
+				if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) {
+					AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr));
+					if (found) {
+						lbValue base = lb_build_expr(p, found->node);
+
+						Type *slice_type = base.type;
+						lbValue len = lb_slice_len(p, base);
+						if (high.value == nullptr) high = len;
+
+						if (!no_indices) {
+							lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+						}
+
+
+						lbValue elem    = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low);
+						lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+						lbAddr slice = lb_add_local_generated(p, slice_type, false);
+						lb_fill_slice(p, slice, elem, new_len);
+						return slice;
+					}
+				}
+			}
+		}
+
+
 		lbValue addr = lb_build_addr_ptr(p, se->expr);
 		lbValue addr = lb_build_addr_ptr(p, se->expr);
 		lbValue base = lb_emit_load(p, addr);
 		lbValue base = lb_emit_load(p, addr);
 		Type *type = base_type(base.type);
 		Type *type = base_type(base.type);

+ 7 - 1
src/llvm_backend.hpp

@@ -27,6 +27,8 @@ enum lbAddrKind {
 	lbAddr_BitField,
 	lbAddr_BitField,
 	lbAddr_Context,
 	lbAddr_Context,
 	lbAddr_SoaVariable,
 	lbAddr_SoaVariable,
+
+	lbAddr_AtomOp_index_set,
 };
 };
 
 
 struct lbAddr {
 struct lbAddr {
@@ -48,6 +50,10 @@ struct lbAddr {
 			lbValue index;
 			lbValue index;
 			Ast *index_expr;
 			Ast *index_expr;
 		} soa;
 		} soa;
+		struct {
+			lbValue index;
+			Ast *node;
+		} index_set;
 	};
 	};
 };
 };
 
 
@@ -333,7 +339,7 @@ void    lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *m
 
 
 void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
 void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
 lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
 lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
-
+lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos);
 
 
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_STARTUP_CONTEXT_PROC_NAME   "__$startup_context"
 #define LB_STARTUP_CONTEXT_PROC_NAME   "__$startup_context"

+ 2 - 0
src/parser.hpp

@@ -21,6 +21,8 @@ enum AddressingMode {
 	                          // 	rhs: acts like OptionalOk
 	                          // 	rhs: acts like OptionalOk
 	Addressing_OptionalOk,    // rhs: acts like a value with an optional boolean part (for existence check)
 	Addressing_OptionalOk,    // rhs: acts like a value with an optional boolean part (for existence check)
 	Addressing_SoaVariable,   // Struct-Of-Arrays indexed variable
 	Addressing_SoaVariable,   // Struct-Of-Arrays indexed variable
+
+	Addressing_AtomOpAssign,  // Specialized for custom atom operations for assignments
 };
 };
 
 
 struct TypeAndValue {
 struct TypeAndValue {

+ 25 - 4
src/types.cpp

@@ -1,5 +1,6 @@
 struct Scope;
 struct Scope;
 struct Ast;
 struct Ast;
+struct Entity;
 
 
 enum BasicKind {
 enum BasicKind {
 	Basic_Invalid,
 	Basic_Invalid,
@@ -123,6 +124,21 @@ enum StructSoaKind {
 	StructSoa_Dynamic = 3,
 	StructSoa_Dynamic = 3,
 };
 };
 
 
+enum TypeAtomOpKind {
+	TypeAtomOp_Invalid,
+	
+	TypeAtomOp_index_get,
+	TypeAtomOp_index_set,
+	TypeAtomOp_slice,
+	TypeAtomOp_index_get_ptr,
+
+	TypeAtomOp_COUNT,
+};
+
+struct TypeAtomOpTable {
+	Entity *op[TypeAtomOp_COUNT];
+};
+
 struct TypeStruct {
 struct TypeStruct {
 	Array<Entity *> fields;
 	Array<Entity *> fields;
 	Array<String>   tags;
 	Array<String>   tags;
@@ -135,6 +151,12 @@ struct TypeStruct {
 
 
 	i64      custom_align;
 	i64      custom_align;
 	Entity * names;
 	Entity * names;
+	
+	TypeAtomOpTable *atom_op_table;
+
+	Type *        soa_elem;
+	i64           soa_count;
+	StructSoaKind soa_kind;
 
 
 	bool are_offsets_set;
 	bool are_offsets_set;
 	bool are_offsets_being_processed;
 	bool are_offsets_being_processed;
@@ -142,10 +164,6 @@ struct TypeStruct {
 	bool is_raw_union;
 	bool is_raw_union;
 	bool is_polymorphic;
 	bool is_polymorphic;
 	bool is_poly_specialized;
 	bool is_poly_specialized;
-
-	StructSoaKind soa_kind;
-	Type *        soa_elem;
-	i64           soa_count;
 };
 };
 
 
 struct TypeUnion {
 struct TypeUnion {
@@ -157,6 +175,9 @@ struct TypeUnion {
 	i64           tag_size;
 	i64           tag_size;
 	Type *        polymorphic_params; // Type_Tuple
 	Type *        polymorphic_params; // Type_Tuple
 	Type *        polymorphic_parent;
 	Type *        polymorphic_parent;
+
+	TypeAtomOpTable *atom_op_table;
+
 	bool          no_nil;
 	bool          no_nil;
 	bool          maybe;
 	bool          maybe;
 	bool          is_polymorphic;
 	bool          is_polymorphic;