Bläddra i källkod

Fix to allow procedure groups on objective-c types

gingerBill 2 år sedan
förälder
incheckning
921530dd01
6 ändrade filer med 229 tillägg och 122 borttagningar
  1. 67 57
      src/check_decl.cpp
  2. 109 61
      src/check_expr.cpp
  3. 48 0
      src/checker.cpp
  4. 2 1
      src/llvm_backend_expr.cpp
  5. 1 1
      src/types.cpp
  6. 2 2
      vendor/darwin/Metal/MetalClasses.odin

+ 67 - 57
src/check_decl.cpp

@@ -757,6 +757,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
 	return link_name;
 }
 
+gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
+	if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
+		return;
+	}
+	if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+		error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+	} else if (ac.objc_type == nullptr) {
+		error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+	} else if (ac.objc_name.len == 0 && ac.objc_type) {
+		error(e->token, "@(objc_name) is required with @(objc_type)");
+	} else {
+		Type *t = ac.objc_type;
+		if (t->kind == Type_Named) {
+			Entity *tn = t->Named.type_name;
+
+			GB_ASSERT(tn->kind == Entity_TypeName);
+
+			if (tn->scope != e->scope) {
+				error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+			} else {
+				mutex_lock(&global_type_name_objc_metadata_mutex);
+				defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+				if (!tn->TypeName.objc_metadata) {
+					tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+				}
+				auto *md = tn->TypeName.objc_metadata;
+				mutex_lock(md->mutex);
+				defer (mutex_unlock(md->mutex));
+
+				if (!ac.objc_is_class_method) {
+					bool ok = true;
+					for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+						if (entry.name == ac.objc_name) {
+							error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+							ok = false;
+							break;
+						}
+					}
+					if (ok) {
+						array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+					}
+				} else {
+					bool ok = true;
+					for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+						if (entry.name == ac.objc_name) {
+							error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+							ok = false;
+							break;
+						}
+					}
+					if (ok) {
+						array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+					}
+				}
+			}
+		}
+	}
+}
+
 gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	GB_ASSERT(e->type == nullptr);
 	if (d->proc_lit->kind != Ast_ProcLit) {
@@ -840,62 +900,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	}
 	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
 
-	if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
-		if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
-			error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
-		} else if (ac.objc_type == nullptr) {
-			error(e->token, "@(objc_name) requires that @(objc_type) to be set");
-		} else if (ac.objc_name.len == 0 && ac.objc_type) {
-			error(e->token, "@(objc_name) is required with @(objc_type)");
-		} else {
-			Type *t = ac.objc_type;
-			if (t->kind == Type_Named) {
-				Entity *tn = t->Named.type_name;
-
-				GB_ASSERT(tn->kind == Entity_TypeName);
-
-				if (tn->scope != e->scope) {
-					error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
-				} else {
-					mutex_lock(&global_type_name_objc_metadata_mutex);
-					defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
-
-					if (!tn->TypeName.objc_metadata) {
-						tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
-					}
-					auto *md = tn->TypeName.objc_metadata;
-					mutex_lock(md->mutex);
-					defer (mutex_unlock(md->mutex));
-
-					if (!ac.objc_is_class_method) {
-						bool ok = true;
-						for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
-							if (entry.name == ac.objc_name) {
-								error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
-								ok = false;
-								break;
-							}
-						}
-						if (ok) {
-							array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
-						}
-					} else {
-						bool ok = true;
-						for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
-							if (entry.name == ac.objc_name) {
-								error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
-								ok = false;
-								break;
-							}
-						}
-						if (ok) {
-							array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
-						}
-					}
-				}
-			}
-		}
-	}
+	check_objc_methods(ctx, e, ac);
 
 	if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
 		error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
@@ -1241,7 +1246,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
 	check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
 }
 
-gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
+gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
 	GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
 	auto *pge = &pg_entity->ProcGroup;
 	String proc_group_name = pg_entity->token.string;
@@ -1366,6 +1371,11 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity,
 		}
 	}
 
+	AttributeContext ac = {};
+	check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
+	check_objc_methods(ctx, pg_entity, ac);
+
+
 }
 
 gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {

+ 109 - 61
src/check_expr.cpp

@@ -6328,9 +6328,46 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 			print_argument_types();
 		}
 
+		if (procs.count == 0) {
+			procs = proc_group_entities_cloned(c, *operand);
+		}
 		if (procs.count > 0) {
 			error_line("Did you mean to use one of the following:\n");
 		}
+		isize max_name_length = 0;
+		isize max_type_length = 0;
+		for (Entity *proc : procs) {
+			Type *t = base_type(proc->type);
+			if (t == t_invalid) continue;
+			String prefix = {};
+			String prefix_sep = {};
+			if (proc->pkg) {
+				prefix = proc->pkg->name;
+				prefix_sep = str_lit(".");
+			}
+			String name = proc->token.string;
+			max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len);
+
+
+
+			gbString pt;
+			if (t->Proc.node != nullptr) {
+				pt = expr_to_string(t->Proc.node);
+			} else {
+				pt = type_to_string(t);
+			}
+
+			max_type_length = gb_max(max_type_length, gb_string_length(pt));
+			gb_string_free(pt);
+		}
+
+		isize max_spaces = gb_max(max_name_length, max_type_length);
+		char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
+		for (isize i = 0; i < max_spaces; i++) {
+			spaces[i] = ' ';
+		}
+		spaces[max_spaces] = 0;
+
 		for (Entity *proc : procs) {
 			TokenPos pos = proc->token.pos;
 			Type *t = base_type(proc->type);
@@ -6350,12 +6387,23 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 				prefix_sep = str_lit(".");
 			}
 			String name = proc->token.string;
+			isize len = prefix.len + prefix_sep.len + name.len;
+
+			int name_padding = cast(int)gb_max(max_name_length - len, 0);
+			int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0);
 
 			char const *sep = "::";
 			if (proc->kind == Entity_Variable) {
 				sep = ":=";
 			}
-			error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
+			error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n",
+			           LIT(prefix), LIT(prefix_sep), LIT(name),
+			           name_padding, spaces,
+			           sep,
+			           pt,
+			           type_padding, spaces,
+			           token_pos_to_string(pos)
+			);
 		}
 		if (procs.count > 0) {
 			error_line("\n");
@@ -9315,13 +9363,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
 	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) {
+	if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) {
 		o->mode = Addressing_Invalid;
 		o->type = t_invalid;
 		o->expr = node;
 		return kind;
 	}
-	if (!is_type_proc(x.type)) {
+	if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) {
 		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);
@@ -9344,76 +9392,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
 		first_arg->state_flags |= StateFlag_SelectorCallExpr;
 	}
 
-	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 (e->kind != Entity_ProcGroup) {
+		Type *pt = base_type(x.type);
+		GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type));
+		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;
-	}
+		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;
+		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)) {
+		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_And};
-				first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+				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 (Ast *arg : ce->args) {
-			bool mix = false;
-			if (first_is_field_value) {
-				mix = arg->kind != Ast_FieldValue;
-			} else {
-				mix = arg->kind == Ast_FieldValue;
+		if (ce->args.count > 0) {
+			bool fail = false;
+			bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+			for (Ast *arg : ce->args) {
+				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 (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);
 			}
 		}
-		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<Ast *>(heap_allocator(), ce->args.count+1);
 	modified_args[0] = first_arg;
 	slice_copy(&modified_args, ce->args, 1);

+ 48 - 0
src/checker.cpp

@@ -2935,6 +2935,54 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
 	return false;
 }
 
+gb_internal DECL_ATTRIBUTE_PROC(proc_group_attribute) {
+	if (name == ATTRIBUTE_USER_TAG_NAME) {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind != ExactValue_String) {
+			error(elem, "Expected a string value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_name") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_String) {
+			if (string_is_valid_identifier(ev.value_string)) {
+				ac->objc_name = ev.value_string;
+			} else {
+				error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+			}
+		} else {
+			error(elem, "Expected a string value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_is_class_method") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_Bool) {
+			ac->objc_is_class_method = ev.value_bool;
+		} else {
+			error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_type") {
+		if (value == nullptr) {
+			error(elem, "Expected a type for '%.*s'", LIT(name));
+		} else {
+			Type *objc_type = check_type(c, value);
+			if (objc_type != nullptr) {
+				if (!has_type_got_objc_class_attribute(objc_type)) {
+					gbString t = type_to_string(objc_type);
+					error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
+					gb_string_free(t);
+				} else {
+					ac->objc_type = objc_type;
+				}
+			}
+		}
+		return true;
+	}
+	return false;
+}
+
+
 gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 	if (name == ATTRIBUTE_USER_TAG_NAME) {
 		ExactValue ev = check_decl_attribute_value(c, value);

+ 2 - 1
src/llvm_backend_expr.cpp

@@ -4519,8 +4519,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 			Selection sel = lookup_field(type, selector, false);
 			GB_ASSERT(sel.entity != nullptr);
 			if (sel.pseudo_field) {
-				GB_ASSERT(sel.entity->kind == Entity_Procedure);
+				GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
 				Entity *e = entity_of_node(sel_node);
+				GB_ASSERT(e->kind == Entity_Procedure);
 				return lb_addr(lb_find_value_from_entity(p->module, e));
 			}
 

+ 1 - 1
src/types.cpp

@@ -3081,7 +3081,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
 				mutex_lock(md->mutex);
 				defer (mutex_unlock(md->mutex));
 				for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
-					GB_ASSERT(entry.entity->kind == Entity_Procedure);
+					GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
 					if (entry.name == field_name) {
 						sel.entity = entry.entity;
 						sel.pseudo_field = true;

+ 2 - 2
vendor/darwin/Metal/MetalClasses.odin

@@ -5388,8 +5388,8 @@ Device_newBufferWithLength :: #force_inline proc "c" (self: ^Device, length: NS.
 
 @(objc_type=Device, objc_name="newBuffer")
 Device_newBuffer :: proc{
-	Device_newBufferWithBytes,
-	Device_newBufferWithBytesNoCopy,
+	// Device_newBufferWithBytes,
+	// Device_newBufferWithBytesNoCopy,
 	Device_newBufferWithSlice,
 	Device_newBufferWithSliceNoCopy,
 	Device_newBufferWithLength,