diff --git a/core/_preload.odin b/core/_preload.odin index 3c1b7569d..0a49d5970 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -877,6 +877,7 @@ __cstring_len :: proc "contextless" (s: cstring) -> int { } __cstring_to_string :: proc "contextless" (s: cstring) -> string { + if s == nil do return ""; ptr := (^byte)(s); n := __cstring_len(s); return transmute(string)raw.String{ptr, n}; diff --git a/core/strings.odin b/core/strings.odin index e1d9ada9c..0bfcccfbc 100644 --- a/core/strings.odin +++ b/core/strings.odin @@ -15,8 +15,8 @@ new_cstring :: proc(s: string) -> cstring { return cstring(&c[0]); } +@(deprecated="Please use a standard cast for cstring to string") to_odin_string :: proc(str: cstring) -> string { - if str == nil do return ""; return string(str); } diff --git a/examples/demo.odin b/examples/demo.odin index 3be85725b..f827cd767 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -778,6 +778,20 @@ cstring_example :: proc() { // 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() { when true { general_stuff(); @@ -792,5 +806,6 @@ main :: proc() { explicit_procedure_overloading(); complete_switch(); cstring_example(); + deprecated_attribute(); } } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index b61bce253..1215e337d 100644 --- a/src/check_decl.cpp +++ b/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); if (d->scope->file != nullptr && e->token.string == "main") { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7d4c759d9..853760204 100644 --- a/src/check_expr.cpp +++ b/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); Type *result_type = data.result_type; diff --git a/src/checker.cpp b/src/checker.cpp index f57e9892e..b4a77de37 100644 --- a/src/checker.cpp +++ b/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); } +// 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) { if (e != nullptr) { @@ -877,6 +900,11 @@ void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) { } identifier->Ident.entity = entity; 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)); } 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; } @@ -1567,6 +1607,7 @@ void check_decl_attributes(Checker *c, Array attributes, DeclAttribut if (value != nullptr) { Operand op = {}; check_expr(c, &op, value); + if (op.mode ) if (op.mode != Addressing_Constant) { error(value, "An attribute element must be constant"); } else { diff --git a/src/checker.hpp b/src/checker.hpp index 90ced6a67..51c81b2a3 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -193,8 +193,6 @@ struct DeclInfo { }; // ProcedureInfo stores the information needed for checking a procedure - - struct ProcedureInfo { AstFile * file; Token token; @@ -356,6 +354,9 @@ AstFile * ast_file_of_filename (CheckerInfo *i, String filename); // IMPORTANT: Only to use once checking is done 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 *scope_lookup_entity (Scope *s, String name); @@ -389,6 +390,7 @@ struct AttributeContext { String link_prefix; isize init_expr_list_count; String thread_local_model; + String deprecated_message; }; AttributeContext make_attribute_context(String link_prefix) { diff --git a/src/entity.cpp b/src/entity.cpp index 14efe6b60..5c7157490 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -75,6 +75,7 @@ struct Entity { AstNode * using_expr; isize order_in_src; + String deprecated_message; union { struct { diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 2c6527f3f..49ba5c353 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -6,6 +6,7 @@ struct AstNode; struct HashKey; struct Type; +struct Entity; bool are_types_identical(Type *x, Type *y); struct Complex128 { @@ -21,9 +22,9 @@ enum ExactValueKind { ExactValue_Float, ExactValue_Complex, 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_Type, + ExactValue_Entity, // TODO(bill): Is this good enough? ExactValue_Count, }; @@ -39,7 +40,7 @@ struct ExactValue { Complex128 value_complex; AstNode * value_compound; 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); case ExactValue_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)); @@ -125,18 +126,18 @@ ExactValue exact_value_pointer(i64 ptr) { 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 result = {ExactValue_Procedure}; result.value_procedure = node; 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) { u64 u = u64_from_string(string); @@ -690,13 +691,6 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { } 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"); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 632d9c0e1..814ae5a7f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -548,17 +548,17 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * break; case ExactValue_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"); break; } - Type *t = core_type(type); if (!is_type_string(type)) { GB_ASSERT(is_type_array(type)); ir_write_str_lit(f, "c\""); ir_print_escape_string(f, str, false, false); 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 // of the .ll file irValue *str_array = ir_add_global_string_array(m, str);