Browse Source

`deprecated` attribute for procedure declarations

gingerBill 7 years ago
parent
commit
9274f29ca9
10 changed files with 89 additions and 24 deletions
  1. 1 0
      core/_preload.odin
  2. 1 1
      core/strings.odin
  3. 15 0
      examples/demo.odin
  4. 1 0
      src/check_decl.cpp
  5. 10 0
      src/check_expr.cpp
  6. 41 0
      src/checker.cpp
  7. 4 2
      src/checker.hpp
  8. 1 0
      src/entity.cpp
  9. 12 18
      src/exact_value.cpp
  10. 3 3
      src/ir_print.cpp

+ 1 - 0
core/_preload.odin

@@ -877,6 +877,7 @@ __cstring_len :: proc "contextless" (s: cstring) -> int {
 }
 }
 
 
 __cstring_to_string :: proc "contextless" (s: cstring) -> string {
 __cstring_to_string :: proc "contextless" (s: cstring) -> string {
+	if s == nil do return "";
 	ptr := (^byte)(s);
 	ptr := (^byte)(s);
 	n := __cstring_len(s);
 	n := __cstring_len(s);
 	return transmute(string)raw.String{ptr, n};
 	return transmute(string)raw.String{ptr, n};

+ 1 - 1
core/strings.odin

@@ -15,8 +15,8 @@ new_cstring :: proc(s: string) -> cstring {
 	return cstring(&c[0]);
 	return cstring(&c[0]);
 }
 }
 
 
+@(deprecated="Please use a standard cast for cstring to string")
 to_odin_string :: proc(str: cstring) -> string {
 to_odin_string :: proc(str: cstring) -> string {
-	if str == nil do return "";
 	return string(str);
 	return string(str);
 }
 }
 
 

+ 15 - 0
examples/demo.odin

@@ -778,6 +778,20 @@ cstring_example :: proc() {
 	// cast(cstring)string is O(N)
 	// cast(cstring)string is O(N)
 }
 }
 
 
+deprecated_attribute :: proc() {
+	@(deprecated="Use foo_v2 instead")
+	foo_v1 :: proc(x: int) {
+		fmt.println("foo_v1");
+	}
+	foo_v2 :: proc(x: int) {
+		fmt.println("foo_v2");
+	}
+
+	// NOTE: Uncomment to see the warning messages
+	// foo_v1(1);
+}
+
+
 main :: proc() {
 main :: proc() {
 	when true {
 	when true {
 		general_stuff();
 		general_stuff();
@@ -792,5 +806,6 @@ main :: proc() {
 		explicit_procedure_overloading();
 		explicit_procedure_overloading();
 		complete_switch();
 		complete_switch();
 		cstring_example();
 		cstring_example();
+		deprecated_attribute();
 	}
 	}
 }
 }

+ 1 - 0
src/check_decl.cpp

@@ -527,6 +527,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	}
 	}
 
 
 
 
+	e->deprecated_message = ac.deprecated_message;
 	ac.link_name = handle_link_name(c, e->token, ac.link_name, ac.link_prefix);
 	ac.link_name = handle_link_name(c, e->token, ac.link_name, ac.link_prefix);
 
 
 	if (d->scope->file != nullptr && e->token.string == "main") {
 	if (d->scope->file != nullptr && e->token.string == "main") {

+ 10 - 0
src/check_expr.cpp

@@ -4943,6 +4943,16 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 		}
 		}
 	}
 	}
 
 
+	// NOTE(bill): Should this be here or on the `add_entity_use`?
+	// if (ce->proc != nullptr) {
+	// 	Entity *e = entity_of_node(&c->info, ce->proc);
+	// 	if (e != nullptr && e->kind == Entity_Procedure) {
+	// 		String msg = e->Procedure.deprecated_message;
+	// 		if (msg.len > 0) {
+	// 			warning(call, "%.*s is deprecated: %.*s", LIT(e->token.string), LIT(msg));
+	// 		}
+	// 	}
+	// }
 
 
 	CallArgumentData data = check_call_arguments(c, operand, proc_type, call);
 	CallArgumentData data = check_call_arguments(c, operand, proc_type, call);
 	Type *result_type = data.result_type;
 	Type *result_type = data.result_type;

+ 41 - 0
src/checker.cpp

@@ -708,6 +708,29 @@ bool is_entity_implicitly_imported(Entity *import_name, Entity *e) {
 	return ptr_set_exists(&import_name->ImportName.scope->implicit, e);
 	return ptr_set_exists(&import_name->ImportName.scope->implicit, e);
 }
 }
 
 
+// Will return nullptr if not found
+Entity *entity_of_node(CheckerInfo *i, AstNode *expr) {
+	expr = unparen_expr(expr);
+	switch (expr->kind) {
+	case_ast_node(ident, Ident, expr);
+		return entity_of_ident(i, expr);
+	case_end;
+	case_ast_node(se, SelectorExpr, expr);
+		AstNode *s = se->selector;
+		while (s->kind == AstNode_SelectorExpr) {
+			s = s->SelectorExpr.selector;
+		}
+		if (s->kind == AstNode_Ident) {
+			return entity_of_ident(i, s);
+		}
+	case_end;
+	case_ast_node(cc, CaseClause, expr);
+		return cc->implicit_entity;
+	case_end;
+	}
+	return nullptr;
+}
+
 
 
 DeclInfo *decl_info_of_entity(CheckerInfo *i, Entity *e) {
 DeclInfo *decl_info_of_entity(CheckerInfo *i, Entity *e) {
 	if (e != nullptr) {
 	if (e != nullptr) {
@@ -877,6 +900,11 @@ void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
 	}
 	}
 	identifier->Ident.entity = entity;
 	identifier->Ident.entity = entity;
 	add_declaration_dependency(c, entity); // TODO(bill): Should this be here?
 	add_declaration_dependency(c, entity); // TODO(bill): Should this be here?
+
+	String dmsg = entity->deprecated_message;
+	if (dmsg.len > 0) {
+		warning(identifier, "%.*s is deprecated: %.*s", LIT(entity->token.string), LIT(dmsg));
+	}
 }
 }
 
 
 
 
@@ -1465,6 +1493,18 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			error(elem, "Expected a string value for '%.*s'", LIT(name));
 			error(elem, "Expected a string value for '%.*s'", LIT(name));
 		}
 		}
 		return true;
 		return true;
+	} else if (name == "deprecated") {
+		if (value.kind == ExactValue_String) {
+			String msg = value.value_string;
+			if (msg.len == 0) {
+				error(elem, "Deprecation message cannot be an empty string");
+			} else {
+				ac->deprecated_message = msg;
+			}
+		} else {
+			error(elem, "Expected a string value for '%.*s'", LIT(name));
+		}
+		return true;
 	}
 	}
 	return false;
 	return false;
 }
 }
@@ -1567,6 +1607,7 @@ void check_decl_attributes(Checker *c, Array<AstNode *> attributes, DeclAttribut
 			if (value != nullptr) {
 			if (value != nullptr) {
 				Operand op = {};
 				Operand op = {};
 				check_expr(c, &op, value);
 				check_expr(c, &op, value);
+				if (op.mode )
 				if (op.mode != Addressing_Constant) {
 				if (op.mode != Addressing_Constant) {
 					error(value, "An attribute element must be constant");
 					error(value, "An attribute element must be constant");
 				} else {
 				} else {

+ 4 - 2
src/checker.hpp

@@ -193,8 +193,6 @@ struct DeclInfo {
 };
 };
 
 
 // ProcedureInfo stores the information needed for checking a procedure
 // ProcedureInfo stores the information needed for checking a procedure
-
-
 struct ProcedureInfo {
 struct ProcedureInfo {
 	AstFile *             file;
 	AstFile *             file;
 	Token                 token;
 	Token                 token;
@@ -356,6 +354,9 @@ AstFile *    ast_file_of_filename   (CheckerInfo *i, String   filename);
 // IMPORTANT: Only to use once checking is done
 // IMPORTANT: Only to use once checking is done
 isize        type_info_index        (CheckerInfo *i, Type *   type, bool error_on_failure = true);
 isize        type_info_index        (CheckerInfo *i, Type *   type, bool error_on_failure = true);
 
 
+// Will return nullptr if not found
+Entity *entity_of_node(CheckerInfo *i, AstNode *expr);
+
 
 
 Entity *current_scope_lookup_entity(Scope *s, String name);
 Entity *current_scope_lookup_entity(Scope *s, String name);
 Entity *scope_lookup_entity        (Scope *s, String name);
 Entity *scope_lookup_entity        (Scope *s, String name);
@@ -389,6 +390,7 @@ struct AttributeContext {
 	String  link_prefix;
 	String  link_prefix;
 	isize   init_expr_list_count;
 	isize   init_expr_list_count;
 	String  thread_local_model;
 	String  thread_local_model;
+	String  deprecated_message;
 };
 };
 
 
 AttributeContext make_attribute_context(String link_prefix) {
 AttributeContext make_attribute_context(String link_prefix) {

+ 1 - 0
src/entity.cpp

@@ -75,6 +75,7 @@ struct Entity {
 	AstNode *  using_expr;
 	AstNode *  using_expr;
 
 
 	isize      order_in_src;
 	isize      order_in_src;
+	String     deprecated_message;
 
 
 	union {
 	union {
 		struct {
 		struct {

+ 12 - 18
src/exact_value.cpp

@@ -6,6 +6,7 @@
 struct AstNode;
 struct AstNode;
 struct HashKey;
 struct HashKey;
 struct Type;
 struct Type;
+struct Entity;
 bool are_types_identical(Type *x, Type *y);
 bool are_types_identical(Type *x, Type *y);
 
 
 struct Complex128 {
 struct Complex128 {
@@ -21,9 +22,9 @@ enum ExactValueKind {
 	ExactValue_Float,
 	ExactValue_Float,
 	ExactValue_Complex,
 	ExactValue_Complex,
 	ExactValue_Pointer,
 	ExactValue_Pointer,
-	ExactValue_Compound, // TODO(bill): Is this good enough?
+	ExactValue_Compound,  // TODO(bill): Is this good enough?
 	ExactValue_Procedure, // TODO(bill): Is this good enough?
 	ExactValue_Procedure, // TODO(bill): Is this good enough?
-	ExactValue_Type,
+	ExactValue_Entity,    // TODO(bill): Is this good enough?
 
 
 	ExactValue_Count,
 	ExactValue_Count,
 };
 };
@@ -39,7 +40,7 @@ struct ExactValue {
 		Complex128    value_complex;
 		Complex128    value_complex;
 		AstNode *     value_compound;
 		AstNode *     value_compound;
 		AstNode *     value_procedure;
 		AstNode *     value_procedure;
-		Type *        value_type;
+		Entity *      value_entity;
 	};
 	};
 };
 };
 
 
@@ -67,8 +68,8 @@ HashKey hash_exact_value(ExactValue v) {
 		return hash_pointer(v.value_compound);
 		return hash_pointer(v.value_compound);
 	case ExactValue_Procedure:
 	case ExactValue_Procedure:
 		return hash_pointer(v.value_procedure);
 		return hash_pointer(v.value_procedure);
-	case ExactValue_Type:
-		return hash_pointer(v.value_type);
+	case ExactValue_Entity:
+		return hash_pointer(v.value_entity);
 	}
 	}
 	return hashing_proc(&v, gb_size_of(ExactValue));
 	return hashing_proc(&v, gb_size_of(ExactValue));
 
 
@@ -125,18 +126,18 @@ ExactValue exact_value_pointer(i64 ptr) {
 	return result;
 	return result;
 }
 }
 
 
-ExactValue exact_value_type(Type *type) {
-	ExactValue result = {ExactValue_Type};
-	result.value_type = type;
-	return result;
-}
-
 ExactValue exact_value_procedure(AstNode *node) {
 ExactValue exact_value_procedure(AstNode *node) {
 	ExactValue result = {ExactValue_Procedure};
 	ExactValue result = {ExactValue_Procedure};
 	result.value_procedure = node;
 	result.value_procedure = node;
 	return result;
 	return result;
 }
 }
 
 
+ExactValue exact_value_entity(Entity *entity) {
+	ExactValue result = {ExactValue_Entity};
+	result.value_entity = entity;
+	return result;
+}
+
 
 
 ExactValue exact_value_integer_from_string(String string) {
 ExactValue exact_value_integer_from_string(String string) {
 	u64 u = u64_from_string(string);
 	u64 u = u64_from_string(string);
@@ -690,13 +691,6 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 		}
 		}
 		break;
 		break;
 	}
 	}
-
-	case ExactValue_Type:
-		switch (op) {
-		case Token_CmpEq: return are_types_identical(x.value_type, y.value_type);
-		case Token_NotEq: return !are_types_identical(x.value_type, y.value_type);
-		}
-		break;
 	}
 	}
 
 
 	GB_PANIC("Invalid comparison");
 	GB_PANIC("Invalid comparison");

+ 3 - 3
src/ir_print.cpp

@@ -548,17 +548,17 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 		break;
 		break;
 	case ExactValue_String: {
 	case ExactValue_String: {
 		String str = value.value_string;
 		String str = value.value_string;
-		if (str.len == 0) {
+		Type *t = core_type(type);
+		if (str.len == 0 && !is_type_cstring(t)) {
 			ir_write_str_lit(f, "zeroinitializer");
 			ir_write_str_lit(f, "zeroinitializer");
 			break;
 			break;
 		}
 		}
-		Type *t = core_type(type);
 		if (!is_type_string(type)) {
 		if (!is_type_string(type)) {
 			GB_ASSERT(is_type_array(type));
 			GB_ASSERT(is_type_array(type));
 			ir_write_str_lit(f, "c\"");
 			ir_write_str_lit(f, "c\"");
 			ir_print_escape_string(f, str, false, false);
 			ir_print_escape_string(f, str, false, false);
 			ir_write_str_lit(f, "\\00\"");
 			ir_write_str_lit(f, "\\00\"");
-		} else if (t == t_cstring) {
+		} else if (is_type_cstring(t)) {
 			// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
 			// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
 			// of the .ll file
 			// of the .ll file
 			irValue *str_array = ir_add_global_string_array(m, str);
 			irValue *str_array = ir_add_global_string_array(m, str);