Add more objc attributes

This commit is contained in:
gingerBill
2022-02-11 22:54:51 +00:00
parent 416413bebf
commit f8afda3b22
24 changed files with 588 additions and 142 deletions
+3 -5
View File
@@ -287,15 +287,13 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
Operand self = {};
check_expr_or_type(c, &self, ce->args[1]);
if (self.mode == Addressing_Type) {
if (!internal_check_is_assignable_to(self.type, t_objc_object)) {
if (!is_type_objc_object(self.type)) {
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if (!(self.type->kind == Type_Named &&
self.type->Named.type_name != nullptr &&
self.type->Named.type_name->TypeName.objc_class_name != "")) {
if (!has_type_got_objc_class_attribute(self.type)) {
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
gb_string_free(t);
@@ -306,7 +304,7 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
} else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s'3 expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind);
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind);
gb_string_free(t);
gb_string_free(e);
return false;
+63
View File
@@ -340,6 +340,10 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
if (e->kind == Entity_TypeName && ac.objc_class != "") {
e->TypeName.objc_class_name = ac.objc_class;
if (type_size_of(e->type) > 0) {
error(e->token, "@(objc_class) marked type must be of zero size");
}
}
}
@@ -822,6 +826,65 @@ 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_class_name.len || ac.objc_type) {
if (ac.objc_class_name.len && ac.objc_name.len) {
error(e->token, "@(objc_class_name) and @(objc_name) may not be allowed at the same time");
} else if (ac.objc_type == nullptr) {
if (ac.objc_name.len) {
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
} else {
error(e->token, "@(objc_class_name) requires that @(objc_type) to be set");
}
} 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) and @(objc_class_name) attributes 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_name.len) {
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_class_name) {
error(e->token, "Previous declaration of @(objc_class_name=\"%.*s\")", LIT(ac.objc_class_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_class_name, e});
}
}
}
}
}
}
switch (e->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
+2
View File
@@ -325,6 +325,8 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
named_type->Named.type_name = e;
GB_ASSERT(original_type->kind == Type_Named);
e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
// TODO(bill): Is this even correct? Or should the metadata be copied?
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
mutex_lock(&ctx->info->gen_types_mutex);
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
+49 -1
View File
@@ -4,7 +4,7 @@
void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
Type *check_type(CheckerContext *ctx, Ast *e);
bool is_operand_value(Operand o) {
switch (o.mode) {
@@ -2740,6 +2740,14 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
return ev;
}
Type *check_decl_attribute_type(CheckerContext *c, Ast *value) {
if (value != nullptr) {
return check_type(c, value);
}
return nullptr;
}
#define ATTRIBUTE_USER_TAG_NAME "tag"
@@ -3039,6 +3047,46 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
error(elem, "Expected a string 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_class_name") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
if (string_is_valid_identifier(ev.value_string)) {
ac->objc_class_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_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;
}
+5 -1
View File
@@ -107,7 +107,6 @@ struct AttributeContext {
String thread_local_model;
String deprecated_message;
String warning_message;
String objc_class;
DeferredProcedure deferred_procedure;
bool is_export : 1;
bool is_static : 1;
@@ -119,6 +118,11 @@ struct AttributeContext {
bool init : 1;
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
String objc_class;
String objc_name;
String objc_class_name;
Type * objc_type;
};
AttributeContext make_attribute_context(String link_prefix) {
+23
View File
@@ -122,6 +122,28 @@ enum ProcedureOptimizationMode : u32 {
ProcedureOptimizationMode_Speed,
};
BlockingMutex global_type_name_objc_metadata_mutex;
struct TypeNameObjCMetadataEntry {
String name;
Entity *entity;
};
struct TypeNameObjCMetadata {
BlockingMutex *mutex;
Array<TypeNameObjCMetadataEntry> type_entries;
Array<TypeNameObjCMetadataEntry> value_entries;
};
TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata);
md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex);
mutex_init(md->mutex);
array_init(&md->type_entries, heap_allocator());
array_init(&md->value_entries, heap_allocator());
return md;
}
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -187,6 +209,7 @@ struct Entity {
String ir_mangled_name;
bool is_type_alias;
String objc_class_name;
TypeNameObjCMetadata *objc_metadata;
} TypeName;
struct {
u64 tags;
+10 -1
View File
@@ -3320,7 +3320,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
GB_PANIC("Unreachable");
Selection sel = lookup_field(tav.type, selector, true);
if (sel.pseudo_field) {
GB_ASSERT(sel.entity->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, sel.entity));
}
GB_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
@@ -3347,6 +3352,10 @@ lbAddr lb_build_addr(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);
return lb_addr(lb_find_value_from_entity(p->module, sel.entity));
}
{
lbAddr addr = lb_build_addr(p, se->expr);
+1 -31
View File
@@ -585,37 +585,6 @@ void usage(String argv0) {
print_usage_line(1, "e.g. odin build -help");
}
bool string_is_valid_identifier(String str) {
if (str.len <= 0) return false;
isize rune_count = 0;
isize w = 0;
isize offset = 0;
while (offset < str.len) {
Rune r = 0;
w = utf8_decode(str.text, str.len, &r);
if (r == GB_RUNE_INVALID) {
return false;
}
if (rune_count == 0) {
if (!rune_is_letter(r)) {
return false;
}
} else {
if (!rune_is_letter(r) && !rune_is_digit(r)) {
return false;
}
}
rune_count += 1;
offset += w;
}
return true;
}
enum BuildFlagKind {
BuildFlag_Invalid,
@@ -2447,6 +2416,7 @@ int main(int arg_count, char const **arg_ptr) {
virtual_memory_init();
mutex_init(&fullpath_mutex);
mutex_init(&hash_exact_value_mutex);
mutex_init(&global_type_name_objc_metadata_mutex);
init_string_buffer_memory();
init_string_interner();
+31
View File
@@ -781,3 +781,34 @@ i32 unquote_string(gbAllocator a, String *s_, u8 quote=0, bool has_carriage_retu
return 2;
}
bool string_is_valid_identifier(String str) {
if (str.len <= 0) return false;
isize rune_count = 0;
isize w = 0;
isize offset = 0;
while (offset < str.len) {
Rune r = 0;
w = utf8_decode(str.text, str.len, &r);
if (r == GB_RUNE_INVALID) {
return false;
}
if (rune_count == 0) {
if (!rune_is_letter(r)) {
return false;
}
} else {
if (!rune_is_letter(r) && !rune_is_digit(r)) {
return false;
}
}
rune_count += 1;
offset += w;
}
return true;
}
+62
View File
@@ -393,6 +393,7 @@ struct Selection {
bool indirect; // Set if there was a pointer deref anywhere down the line
u8 swizzle_count; // maximum components = 4
u8 swizzle_indices; // 2 bits per component, representing which swizzle index
bool pseudo_field;
};
Selection empty_selection = {0};
@@ -2782,6 +2783,7 @@ Selection lookup_field_from_index(Type *type, i64 index) {
}
Entity *scope_lookup_current(Scope *s, String const &name);
bool has_type_got_objc_class_attribute(Type *t);
Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
GB_ASSERT(type_ != nullptr);
@@ -2794,9 +2796,40 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
bool is_ptr = type != type_;
sel.indirect = sel.indirect || is_ptr;
Type *original_type = type;
type = base_type(type);
if (is_type) {
if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) {
Entity *e = original_type->Named.type_name;
GB_ASSERT(e->kind == Entity_TypeName);
if (e->TypeName.objc_metadata) {
auto *md = e->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
GB_ASSERT(entry.entity->kind == Entity_Procedure);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
return sel;
}
}
}
if (type->kind == Type_Struct) {
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->flags&EntityFlag_Using) {
sel = lookup_field_with_selection(f->type, field_name, is_type, sel, allow_blank_ident);
if (sel.entity) {
return sel;
}
}
}
}
}
if (is_type_enum(type)) {
// NOTE(bill): These may not have been added yet, so check in case
for_array(i, type->Enum.fields) {
@@ -2843,6 +2876,24 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
} else if (type->kind == Type_Union) {
} else if (type->kind == Type_Struct) {
if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) {
Entity *e = original_type->Named.type_name;
GB_ASSERT(e->kind == Entity_TypeName);
if (e->TypeName.objc_metadata) {
auto *md = e->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
GB_ASSERT(entry.entity->kind == Entity_Procedure);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
return sel;
}
}
}
}
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
@@ -3792,6 +3843,17 @@ bool is_type_subtype_of(Type *src, Type *dst) {
}
bool has_type_got_objc_class_attribute(Type *t) {
return t->kind == Type_Named && t->Named.type_name != nullptr && t->Named.type_name->TypeName.objc_class_name != "";
}
bool is_type_objc_object(Type *t) {
bool internal_check_is_assignable_to(Type *src, Type *dst);
return internal_check_is_assignable_to(t, t_objc_object);
}
Type *get_struct_field_type(Type *t, isize index) {
t = base_type(type_deref(t));