Browse Source

`deferred` procedure attribute

gingerBill 6 years ago
parent
commit
95873e66ab
5 changed files with 147 additions and 10 deletions
  1. 5 0
      src/check_decl.cpp
  2. 73 2
      src/checker.cpp
  3. 1 0
      src/checker.hpp
  4. 9 0
      src/entity.cpp
  5. 59 8
      src/ir.cpp

+ 5 - 0
src/check_decl.cpp

@@ -633,6 +633,11 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 		e->Procedure.link_name = ac.link_name;
 	}
 
+	if (ac.deferred_procedure != nullptr) {
+		e->Procedure.deferred_procedure = ac.deferred_procedure;
+		array_add(&ctx->checker->procs_with_deferred_to_check, e);
+	}
+
 	if (is_foreign) {
 		String name = e->token.string;
 		if (e->Procedure.link_name.len > 0) {

+ 73 - 2
src/checker.cpp

@@ -778,6 +778,7 @@ void init_checker(Checker *c, Parser *parser) {
 	init_checker_info(&c->info);
 
 	array_init(&c->procs_to_check, a);
+	array_init(&c->procs_with_deferred_to_check, a);
 
 	// NOTE(bill): Is this big enough or too small?
 	isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope));
@@ -793,6 +794,7 @@ void destroy_checker(Checker *c) {
 	destroy_checker_info(&c->info);
 
 	array_free(&c->procs_to_check);
+	array_free(&c->procs_with_deferred_to_check);
 
 	destroy_checker_context(&c->init_ctx);
 }
@@ -1030,6 +1032,10 @@ void add_entity_use(CheckerContext *c, Ast *identifier, Entity *entity) {
 	}
 	entity->flags |= EntityFlag_Used;
 	add_declaration_dependency(c, entity);
+	if (entity_has_deferred_procedure(entity)) {
+		Entity *deferred = entity->Procedure.deferred_procedure;
+		add_entity_use(c, nullptr, deferred);
+	}
 }
 
 
@@ -1890,9 +1896,24 @@ DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
 }
 
 DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
-	ExactValue ev = check_decl_attribute_value(c, value);
+	ExactValue ev = {};
+	if (name != "deferred") {
+		ev = check_decl_attribute_value(c, value);
+	}
 
-	if (name == "link_name") {
+	if (name == "deferred") {
+		if (value != nullptr) {
+			Operand o = {};
+			check_expr(c, &o, value);
+			Entity *e = entity_of_ident(o.expr);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				ac->deferred_procedure = e;
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	} else if (name == "link_name") {
 		if (ev.kind == ExactValue_String) {
 			ac->link_name = ev.value_string;
 			if (!is_foreign_name_valid(ac->link_name)) {
@@ -3539,6 +3560,56 @@ void check_parsed_files(Checker *c) {
 		}
 	}
 
+	TIME_SECTION("check deferred procedures");
+	for_array(i, c->procs_with_deferred_to_check) {
+		Entity *src = c->procs_with_deferred_to_check[i];
+		GB_ASSERT(src->kind == Entity_Procedure);
+
+		Entity *dst = src->Procedure.deferred_procedure;
+		GB_ASSERT(dst != nullptr);
+		GB_ASSERT(dst->kind == Entity_Procedure);
+
+		if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) {
+			error(src->token, "'deferred' cannot be used with a polymorphic procedure");
+			continue;
+		}
+
+		GB_ASSERT(is_type_proc(src->type));
+		GB_ASSERT(is_type_proc(dst->type));
+		Type *src_results = base_type(src->type)->Proc.results;
+		Type *dst_params = base_type(dst->type)->Proc.params;
+		if (src_results == nullptr && dst_params == nullptr) {
+			// Okay
+			continue;
+		}
+		if ((src_results == nullptr && dst_params != nullptr) ||
+		    (src_results != nullptr && dst_params == nullptr)) {
+			error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
+			continue;
+		}
+
+		GB_ASSERT(src_results->kind == Type_Tuple);
+		GB_ASSERT(dst_params->kind == Type_Tuple);
+
+		auto const &sv = src_results->Tuple.variables;
+		auto const &dv = dst_params->Tuple.variables;
+
+		if (are_types_identical(src_results, dst_params)) {
+			// Okay!
+		} else {
+			gbString s = type_to_string(src_results);
+			gbString d = type_to_string(dst_params);
+			error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
+			      LIT(src->token.string), LIT(dst->token.string),
+			      s, d
+			);
+			gb_string_free(d);
+			gb_string_free(s);
+			continue;
+		}
+	}
+
+
 	TIME_SECTION("check entry point");
 	if (!build_context.is_dll) {
 		Scope *s = c->info.init_scope;

+ 1 - 0
src/checker.hpp

@@ -477,6 +477,7 @@ struct Checker {
 	CheckerInfo info;
 
 	Array<ProcInfo> procs_to_check;
+	Array<Entity *> procs_with_deferred_to_check;
 
 	gbAllocator    allocator;
 	CheckerContext init_ctx;

+ 9 - 0
src/entity.cpp

@@ -127,6 +127,7 @@ struct Entity {
 			Ast *   foreign_library_ident;
 			String  link_name;
 			String  link_prefix;
+			Entity *deferred_procedure;
 			bool    is_foreign;
 			bool    is_export;
 		} Procedure;
@@ -184,6 +185,14 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 	return name[0] != '_';
 }
 
+bool entity_has_deferred_procedure(Entity *e) {
+	GB_ASSERT(e != nullptr);
+	if (e->kind == Entity_Procedure) {
+		return e->Procedure.deferred_procedure != nullptr;
+	}
+	return false;
+}
+
 
 gb_global u64 global_entity_id = 0;
 

+ 59 - 8
src/ir.cpp

@@ -859,6 +859,10 @@ void     ir_pop_debug_location  (irModule *m);
 irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id);
 irDebugInfo *ir_add_debug_info_file(irModule *module, AstFile *file);
 irDebugInfo *ir_add_debug_info_proc(irProcedure *proc);
+void ir_emit_increment(irProcedure *proc, irValue *addr);
+irValue *ir_emit_array_ep(irProcedure *proc, irValue *s, irValue *index);
+irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index);
+irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index);
 
 irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t);
 
@@ -1274,6 +1278,17 @@ irValue *ir_emit(irProcedure *proc, irValue *instr) {
 	return instr;
 }
 
+irValue *ir_de_emit(irProcedure *proc, irValue *instr) {
+	GB_ASSERT(instr->kind == irValue_Instr);
+	irModule *m = proc->module;
+	irBlock *b = proc->curr_block;
+	GB_ASSERT(b != nullptr);
+	irInstr *i = ir_get_last_instr(b);
+	GB_ASSERT(i == &instr->Instr);
+	array_pop(&b->instrs);
+	return instr;
+}
+
 
 
 irValue *ir_const_int(i64 i) {
@@ -2800,6 +2815,28 @@ irValue *ir_find_or_generate_context_ptr(irProcedure *proc) {
 	return c;
 }
 
+Array<irValue *> ir_value_to_array(irProcedure *p, irValue *value) {
+	Array<irValue *> array = {};
+	Type *t = base_type(ir_type(value));
+	if (t == nullptr) {
+		// Do nothing
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->kind == Type_Tuple);
+		auto *rt = &t->Tuple;
+		if (rt->variables.count > 0) {
+			array = array_make<irValue *>(ir_allocator(), rt->variables.count);
+			for_array(i, rt->variables) {
+				irValue *elem = ir_emit_struct_ev(p, value, cast(i32)i);
+				array[i] = elem;
+			}
+		}
+	} else {
+		array = array_make<irValue *>(ir_allocator(), 1);
+		array[0] = value;
+	}
+	return array;
+}
+
 
 irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, ProcInlining inlining = ProcInlining_none) {
 	Type *pt = base_type(ir_type(value));
@@ -2847,18 +2884,36 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 		inlining = p->inlining;
 	}
 
+	irValue *result = nullptr;
+
 	Type *abi_rt = pt->Proc.abi_compat_result_type;
 	Type *rt = reduce_tuple_to_single_type(results);
 	if (pt->Proc.return_by_pointer) {
 		irValue *return_ptr = ir_add_local_generated(p, rt);
 		GB_ASSERT(is_type_pointer(ir_type(return_ptr)));
 		ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining));
-		return ir_emit_load(p, return_ptr);
+		result = ir_emit_load(p, return_ptr);
+	} else {
+		result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining));
+		if (abi_rt != results) {
+			result = ir_emit_transmute(p, result, rt);
+		}
 	}
 
-	irValue *result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining));
-	if (abi_rt != results) {
-		result = ir_emit_transmute(p, result, rt);
+	if (value->kind == irValue_Proc) {
+		irProcedure *the_proc = &value->Proc;
+		Entity *e = the_proc->entity;
+		if (entity_has_deferred_procedure(e)) {
+			Entity *deferred_entity = e->Procedure.deferred_procedure;
+			irValue **deferred_found = map_get(&p->module->values, hash_entity(deferred_entity));
+			GB_ASSERT(deferred_found != nullptr);
+			irValue *deferred = *deferred_found;
+
+			Array<irValue *> result_as_args = ir_value_to_array(p, result);
+
+			irValue *deferred_call = ir_de_emit(p, ir_emit_call(p, deferred, result_as_args));
+			ir_add_defer_instr(p, p->scope_index, deferred_call);
+		}
 	}
 
 	return result;
@@ -3396,10 +3451,6 @@ irValue *ir_map_cap(irProcedure *proc, irValue *value) {
 }
 
 
-void ir_emit_increment(irProcedure *proc, irValue *addr);
-irValue *ir_emit_array_ep(irProcedure *proc, irValue *s, irValue *index);
-irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index);
-irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index);
 
 struct irLoopData {
 	irValue *idx_addr;