Browse Source

Replace `deferred` with `deferred_none`, `deferred_in`, `deferred_out`

gingerBill 6 years ago
parent
commit
8fc24fd6f2
6 changed files with 169 additions and 41 deletions
  1. 1 1
      examples/demo/demo.odin
  2. 1 1
      src/check_decl.cpp
  3. 138 34
      src/checker.cpp
  4. 12 1
      src/checker.hpp
  5. 3 2
      src/entity.cpp
  6. 14 2
      src/ir.cpp

+ 1 - 1
examples/demo/demo.odin

@@ -872,7 +872,7 @@ diverging_procedures :: proc() {
 }
 
 deferred_procedure_associations :: proc() {
-	@(deferred=closure)
+	@(deferred_out=closure)
 	open :: proc(s: string) -> bool {
 		fmt.println(s);
 		return true;

+ 1 - 1
src/check_decl.cpp

@@ -633,7 +633,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 		e->Procedure.link_name = ac.link_name;
 	}
 
-	if (ac.deferred_procedure != nullptr) {
+	if (ac.deferred_procedure.entity != nullptr) {
 		e->Procedure.deferred_procedure = ac.deferred_procedure;
 		array_add(&ctx->checker->procs_with_deferred_to_check, e);
 	}

+ 138 - 34
src/checker.cpp

@@ -1038,7 +1038,7 @@ 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;
+		Entity *deferred = entity->Procedure.deferred_procedure.entity;
 		add_entity_use(c, nullptr, deferred);
 	}
 }
@@ -1933,24 +1933,71 @@ DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
 }
 
 DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
-	ExactValue ev = {};
-	if (name != "deferred") {
-		ev = check_decl_attribute_value(c, value);
-	}
-
 	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;
+				warning(elem, "'%.*s' is deprecated, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name));
+				if (ac->deferred_procedure.entity != nullptr) {
+					error(elem, "Previous usage of a 'deferred_*' attribute");
+				}
+				ac->deferred_procedure.kind = DeferredProcedure_out;
+				ac->deferred_procedure.entity = e;
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	} else if (name == "deferred_none") {
+		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.kind = DeferredProcedure_none;
+				ac->deferred_procedure.entity = e;
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	} else if (name == "deferred_in") {
+		if (value != nullptr) {
+			Operand o = {};
+			check_expr(c, &o, value);
+			Entity *e = entity_of_ident(o.expr);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				if (ac->deferred_procedure.entity != nullptr) {
+					error(elem, "Previous usage of a 'deferred_*' attribute");
+				}
+				ac->deferred_procedure.kind = DeferredProcedure_in;
+				ac->deferred_procedure.entity = e;
+				return true;
+			}
+		}
+		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
+		return false;
+	} else if (name == "deferred_out") {
+		if (value != nullptr) {
+			Operand o = {};
+			check_expr(c, &o, value);
+			Entity *e = entity_of_ident(o.expr);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				if (ac->deferred_procedure.entity != nullptr) {
+					error(elem, "Previous usage of a 'deferred_*' attribute");
+				}
+				ac->deferred_procedure.kind = DeferredProcedure_out;
+				ac->deferred_procedure.entity = e;
 				return true;
 			}
 		}
 		error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
 		return false;
 	} else if (name == "link_name") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+
 		if (ev.kind == ExactValue_String) {
 			ac->link_name = ev.value_string;
 			if (!is_foreign_name_valid(ac->link_name)) {
@@ -1961,6 +2008,8 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		}
 		return true;
 	} else if (name == "link_prefix") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+
 		if (ev.kind == ExactValue_String) {
 			ac->link_prefix = ev.value_string;
 			if (!is_foreign_name_valid(ac->link_prefix)) {
@@ -1971,6 +2020,8 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		}
 		return true;
 	} else if (name == "deprecated") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+
 		if (ev.kind == ExactValue_String) {
 			String msg = ev.value_string;
 			if (msg.len == 0) {
@@ -3617,47 +3668,100 @@ void check_parsed_files(Checker *c) {
 		Entity *src = c->procs_with_deferred_to_check[i];
 		GB_ASSERT(src->kind == Entity_Procedure);
 
-		Entity *dst = src->Procedure.deferred_procedure;
+		DeferredProcedureKind dst_kind = src->Procedure.deferred_procedure.kind;
+		Entity *dst = src->Procedure.deferred_procedure.entity;
 		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");
+			switch (dst_kind) {
+			case DeferredProcedure_none:
+				error(src->token, "'deferred_none' cannot be used with a polymorphic procedure");
+				break;
+			case DeferredProcedure_in:
+				error(src->token, "'deferred_in' cannot be used with a polymorphic procedure");
+				break;
+			case DeferredProcedure_out:
+				error(src->token, "'deferred_out' cannot be used with a polymorphic procedure");
+				break;
+			}
 			continue;
 		}
 
 		GB_ASSERT(is_type_proc(src->type));
 		GB_ASSERT(is_type_proc(dst->type));
+		Type *src_params = base_type(src->type)->Proc.params;
 		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);
+		if (dst_kind == DeferredProcedure_none) {
+			if (dst_params == nullptr) {
+				// Okay
+				continue;
+			}
 
-		auto const &sv = src_results->Tuple.variables;
-		auto const &dv = dst_params->Tuple.variables;
+			error(src->token, "Deferred procedure '%.*s' must have no input parameters", LIT(dst->token.string));
+		} else if (dst_kind == DeferredProcedure_in) {
+			if (src_params == nullptr && dst_params == nullptr) {
+				// Okay
+				continue;
+			}
+			if ((src_params == nullptr && dst_params != nullptr) ||
+			    (src_params != nullptr && dst_params == nullptr)) {
+				error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
+				continue;
+			}
 
-		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;
+			GB_ASSERT(src_params->kind == Type_Tuple);
+			GB_ASSERT(dst_params->kind == Type_Tuple);
+
+			auto const &sv = src_params->Tuple.variables;
+			auto const &dv = dst_params->Tuple.variables;
+
+			if (are_types_identical(src_params, dst_params)) {
+				// Okay!
+			} else {
+				gbString s = type_to_string(src_params);
+				gbString d = type_to_string(dst_params);
+				error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs 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;
+			}
+
+		} else if (dst_kind == DeferredProcedure_out) {
+			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;
+			}
 		}
 	}
 

+ 12 - 1
src/checker.hpp

@@ -288,13 +288,24 @@ struct BlockLabel {
 	Ast *label; //  Ast_Label;
 };
 
+enum DeferredProcedureKind {
+	DeferredProcedure_none,
+	DeferredProcedure_in,
+	DeferredProcedure_out,
+};
+struct DeferredProcedure {
+	DeferredProcedureKind kind;
+	Entity *entity;
+};
+
+
 struct AttributeContext {
 	String  link_name;
 	String  link_prefix;
 	isize   init_expr_list_count;
 	String  thread_local_model;
 	String  deprecated_message;
-	Entity *deferred_procedure;
+	DeferredProcedure deferred_procedure;
 };
 
 AttributeContext make_attribute_context(String link_prefix) {

+ 3 - 2
src/entity.cpp

@@ -77,6 +77,7 @@ struct ParameterValue {
 };
 
 
+
 // An Entity is a named "thing" in the language
 struct Entity {
 	EntityKind  kind;
@@ -128,7 +129,7 @@ struct Entity {
 			Ast *   foreign_library_ident;
 			String  link_name;
 			String  link_prefix;
-			Entity *deferred_procedure;
+			DeferredProcedure deferred_procedure;
 			bool    is_foreign;
 			bool    is_export;
 		} Procedure;
@@ -189,7 +190,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 bool entity_has_deferred_procedure(Entity *e) {
 	GB_ASSERT(e != nullptr);
 	if (e->kind == Entity_Procedure) {
-		return e->Procedure.deferred_procedure != nullptr;
+		return e->Procedure.deferred_procedure.entity != nullptr;
 	}
 	return false;
 }

+ 14 - 2
src/ir.cpp

@@ -2905,12 +2905,24 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 		irProcedure *the_proc = &value->Proc;
 		Entity *e = the_proc->entity;
 		if (entity_has_deferred_procedure(e)) {
-			Entity *deferred_entity = e->Procedure.deferred_procedure;
+			DeferredProcedureKind kind = e->Procedure.deferred_procedure.kind;
+			Entity *deferred_entity = e->Procedure.deferred_procedure.entity;
 			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);
+
+			auto in_args = args;
+			Array<irValue *> result_as_args = {};
+			switch (kind) {
+			case DeferredProcedure_none:
+				break;
+			case DeferredProcedure_in:
+				result_as_args = in_args;
+			case DeferredProcedure_out:
+				result_as_args = ir_value_to_array(p, result);
+				break;
+			}
 
 			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);