From 2ddb27869bd704d28e4704648c26b17d00ba2ff5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Oct 2018 18:44:28 +0100 Subject: [PATCH] Built-in procedure `#defined` --- examples/demo/demo.odin | 14 ++++++++++ src/check_expr.cpp | 57 ++++++++++++++++++++++++++++++++++++++++- src/ir.cpp | 53 +++++++++++++++++++------------------- src/ir_print.cpp | 9 ++++--- src/parser.cpp | 3 +++ src/parser.hpp | 1 + 6 files changed, 105 insertions(+), 32 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 30c055398..3245ce1dd 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -110,6 +110,20 @@ general_stuff :: proc() { My_Struct :: struct{x: int}; #assert(My_Struct != struct{x: int}); } + + { + X :: 123; + when #defined(X) { + fmt.println("X is defined"); + } else { + fmt.println("X is not defined"); + } + when #defined(Y) { + fmt.println("Y is defined"); + } else { + fmt.println("Y is not defined"); + } + } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0e4833ef1..6b2a07a86 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2885,6 +2885,36 @@ bool is_type_normal_pointer(Type *ptr, Type **elem) { return false; } +bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **out_scope = nullptr) { + switch (node->kind) { + case_ast_node(i, Ident, node); + String name = i->token.string; + if (nested) { + Entity *e = scope_lookup_current(s, name); + if (e != nullptr) { + if (out_scope) *out_scope = e->scope; + return true; + } + } else { + Entity *e = scope_lookup(s, name); + if (e != nullptr) { + if (out_scope) *out_scope = e->scope; + return true; + } + } + case_end; + case_ast_node(se, SelectorExpr, node); + Ast *lhs = se->expr; + Ast *rhs = se->selector; + Scope *lhs_scope = nullptr; + if (check_identifier_exists(s, lhs, nested, &lhs_scope)) { + return check_identifier_exists(lhs_scope, rhs, true); + } + case_end; + } + return false; +} + bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) { ast_node(ce, CallExpr, call); @@ -2920,6 +2950,15 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_len: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; + + case BuiltinProc_DIRECTIVE: { + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name; + if (name == "defined") { + break; + } + /*fallthrough*/ + } default: if (ce->args.count > 0) { check_multi_expr(c, operand, ce->args[0]); @@ -2984,6 +3023,22 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_untyped_bool; operand->mode = Addressing_Constant; + } else if (name == "defined") { + if (ce->args.count != 1) { + error(call, "'#defined' expects 1 argument, got %td", ce->args.count); + return false; + } + Ast *arg = unparen_expr(ce->args[0]); + if (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr) { + error(call, "'#defined' expects an identifier or selector expression, got %s", LIT(ast_strings[arg->kind])); + return false; + } + + bool is_defined = check_identifier_exists(c->scope, arg); + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + operand->value = exact_value_bool(is_defined); + } else { GB_PANIC("Unhandled #%.*s", LIT(name)); } @@ -4957,7 +5012,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { ce->proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, ce->proc); String name = bd->name; - if (name == "location" || name == "assert") { + if (name == "location" || name == "assert" || name == "defined") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; operand->expr = ce->proc; diff --git a/src/ir.cpp b/src/ir.cpp index b1c39a6f8..e6e30283f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -26,7 +26,7 @@ struct irModule { Map anonymous_proc_lits; // Key: Ast * irDebugInfo * debug_compile_unit; - Array debug_location_stack; + Array debug_location_stack; i32 global_string_index; @@ -598,7 +598,7 @@ struct irDebugInfo { irDebugInfo *file; irDebugInfo *scope; } LexicalBlock; - + struct { String name; i32 size; @@ -659,6 +659,8 @@ struct irDebugInfo { }; }; +static irDebugInfo IR_DEBUG_INFO_EMPTY = {}; + struct irGen { irModule module; @@ -1692,7 +1694,7 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) { case Basic_i16: case Basic_i32: - case Basic_i64: + case Basic_i64: case Basic_int: case Basic_rune: case Basic_typeid: @@ -1712,7 +1714,7 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) { // case Basic_complex32: case Basic_complex64: - case Basic_complex128: + case Basic_complex128: case Basic_cstring: case Basic_string: case Basic_any: @@ -1916,7 +1918,7 @@ irDebugInfo *ir_add_debug_info_type_bit_field(irModule *module, Type *type, Enti 0, nullptr, di); - // NOTE(lachsinc): Above calls BitFieldValues type_size_of() which returns size in bits, + // NOTE(lachsinc): Above calls BitFieldValues type_size_of() which returns size in bits, // replace with its true bit value here.. field_di->DerivedType.size = size; field_di->DerivedType.offset = offset; // Offset stored in bits already, no need to convert @@ -1946,7 +1948,7 @@ irDebugInfo *ir_add_debug_info_type_bit_set(irModule *module, Type *type, Entity GB_ASSERT(elem_type->Enum.fields.count == base->BitSet.upper + 1); } } - + irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType); di->CompositeType.name = named != nullptr ? named->Named.name : str_lit("bit_set"); di->CompositeType.tag = irDebugBasicEncoding_structure_type; @@ -1974,7 +1976,7 @@ irDebugInfo *ir_add_debug_info_type_bit_set(irModule *module, Type *type, Entity map_set(&module->debug_info, hash_pointer(field_di), field_di); array_add(&elements_di->DebugInfoArray.elements, field_di); } - + return di; } @@ -2064,7 +2066,7 @@ irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) { di->CompositeType.name = type->Basic.name; di->CompositeType.tag = irDebugBasicEncoding_structure_type; di->CompositeType.size = ir_debug_size_bits(type); - + Type *field_type = nullptr; if (type->Basic.kind == Basic_complex64) { field_type = t_f32; @@ -2421,7 +2423,7 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irD if (is_type_map(type)) { // TODO(lachsinc): Looks like "generated_struct_type" map.entries.data is just a u8*, we could - // always look at the map header and create the debug info manually (if we + // always look at the map header and create the debug info manually (if we // want struct members to be interpreted as the correct type). // Also; are hashes meant to be interpreted as bool*'s ?? or is that simply slot occupied data? return ir_add_debug_info_type(module, type->Map.generated_struct_type, e, scope, file); @@ -2434,7 +2436,7 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irD irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_BasicType); di->BasicType.encoding = irDebugBasicEncoding_unsigned; // di->BasicType.name = str_lit("todo"); - di->BasicType.size = base->BitFieldValue.bits; + di->BasicType.size = base->BitFieldValue.bits; map_set(&module->debug_info, hash_type(type), di); return di; } @@ -2544,7 +2546,7 @@ irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id) { di->LocalVariable.pos = e->token.pos; di->LocalVariable.arg = arg_id; di->LocalVariable.type = ir_add_debug_info_type(module, e->type, e, scope, file); // TODO(lachsinc): Is this the correct entity to pass? Or do we want a TypeName ?? - + map_set(&module->debug_info, hash_entity(e), di); return di; } @@ -2561,12 +2563,12 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc) { CheckerInfo *info = proc->module->info; String filename = proc->entity->token.pos.file; AstFile *f = ast_file_of_filename(info, filename); - irDebugInfo *file = nullptr; + irDebugInfo *file = nullptr; if (f) { file = ir_add_debug_info_file(proc->module, f); } // TODO(lachsinc): Should scope be made separate to file? - irDebugInfo *scope = file; + irDebugInfo *scope = file; irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Proc); map_set(&proc->module->debug_info, hash_entity(entity), di); @@ -2591,7 +2593,7 @@ irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *sco return *existing; } irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Location); - di->Location.pos = ast_token(node).pos; + di->Location.pos = ast_token(node).pos; di->Location.scope = scope; map_set(&m->debug_info, hash_node(node), di); return di; @@ -2613,8 +2615,8 @@ void ir_pop_debug_location(irModule *m) { // //////////////////////////////////////////////////////////////// -irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array args, Ast *expr = nullptr); -irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array args, Ast *expr = nullptr); +irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array args, Ast *expr = nullptr, ProcInlining inlining = ProcInlining_none); +irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array args, Ast *expr = nullptr, ProcInlining inlining = ProcInlining_none); irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) { @@ -2668,7 +2670,8 @@ void ir_emit_zero_init(irProcedure *p, irValue *address, Ast *expr) { args[1] = ir_const_int(type_size_of(t)); AstPackage *pkg = get_core_package(p->module->info, str_lit("mem")); if (p->entity != nullptr && p->entity->token.string != "zero" && p->entity->pkg != pkg) { - ir_emit_package_call(p, "mem", "zero", args, expr); + irValue *v = ir_emit_package_call(p, "mem", "zero", args, expr, ProcInlining_no_inline); + // v->loc = &IR_DEBUG_INFO_EMPTY; // NOTE(bill): remove debug location } ir_emit(p, ir_instr_zero_init(p, address)); } @@ -2803,7 +2806,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro return result; } -irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array args, Ast *expr) { +irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array args, Ast *expr, ProcInlining inlining) { String name = make_string_c(cast(char *)name_); AstPackage *p = proc->module->info->runtime_package; @@ -2811,10 +2814,10 @@ irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Arraymodule->values, hash_entity(e)); GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name)); irValue *gp = *found; - irValue *call = ir_emit_call(proc, gp, args); + irValue *call = ir_emit_call(proc, gp, args, inlining); return call; } -irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array args, Ast *expr) { +irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array args, Ast *expr, ProcInlining inlining) { String name = make_string_c(cast(char *)name_); String package_name = make_string_c(cast(char *)package_name_); @@ -2823,7 +2826,7 @@ irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char irValue **found = map_get(&proc->module->values, hash_entity(e)); GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name)); irValue *gp = *found; - irValue *call = ir_emit_call(proc, gp, args); + irValue *call = ir_emit_call(proc, gp, args, inlining); return call; } @@ -6766,7 +6769,6 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { ir_emit_bounds_check(proc, ast_token(ie->index), index, len); } return ir_addr(elem); - break; } case Type_Slice: { @@ -6785,7 +6787,6 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { ir_emit_bounds_check(proc, ast_token(ie->index), index, len); irValue *v = ir_emit_ptr_offset(proc, elem, index); return ir_addr(v); - break; } case Type_DynamicArray: { @@ -6804,7 +6805,6 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { ir_emit_bounds_check(proc, ast_token(ie->index), index, len); irValue *v = ir_emit_ptr_offset(proc, elem, index); return ir_addr(v); - break; } @@ -6829,7 +6829,6 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { ir_emit_bounds_check(proc, ast_token(ie->index), index, len); return ir_addr(ir_emit_ptr_offset(proc, elem, index)); - break; } } case_end; @@ -8540,7 +8539,7 @@ void ir_begin_procedure_body(irProcedure *proc) { if (proc->module->generate_debug_info && proc->entity && proc->entity->identifier) { // TODO(lachsinc): Better way to determine if these procs are main/runtime_startup. // TODO(lachsinc): Passing the file for the scope may not be correct for nested procedures? This should probably be // handled all inside push_debug_location, with just the Ast * we can pull out everything we need to construct scope/file debug info etc. - ir_add_debug_info_proc(proc); + ir_add_debug_info_proc(proc); ir_push_debug_location(proc->module, proc->entity->identifier, proc->debug_scope); GB_ASSERT_NOT_NULL(proc->debug_scope); } else { @@ -8717,7 +8716,7 @@ void ir_build_proc(irValue *value, irProcedure *parent) { proc->module->stmt_state_flags = prev_stmt_state_flags; } - // NOTE(lachsinc): For now we pop the debug location inside ir_end_procedure_body(). + // NOTE(lachsinc): For now we pop the debug location inside ir_end_procedure_body(). // This may result in debug info being missing for below. if (proc->type->Proc.has_proc_default_values) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7c01d6a46..177ae0aa1 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -221,9 +221,10 @@ bool ir_print_debug_location(irFileBuffer *f, irModule *m, irValue *v) { GB_ASSERT(v->kind == irValue_Instr); if (v->loc != nullptr) { - GB_ASSERT(v->loc->kind == irDebugInfo_Location); - ir_fprintf(f, ", !dbg !%d", v->loc->id); - return true; + if (v->loc->kind == irDebugInfo_Location) { + ir_fprintf(f, ", !dbg !%d", v->loc->id); + return true; + } } else { irProcedure *proc = v->Instr.block->proc; GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0)); @@ -1812,7 +1813,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { irDebugInfo **lookup_di = map_get(&m->debug_info, hash_entity(e)); GB_ASSERT_NOT_NULL(*lookup_di); irDebugInfo* local_var_di = *lookup_di; - + ir_write_str_lit(f, "call void @llvm.dbg.declare("); ir_write_str_lit(f, "metadata "); ir_print_type(f, m, vt); diff --git a/src/parser.cpp b/src/parser.cpp index 231a40e99..38678a6dc 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1677,6 +1677,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { } else if (name.string == "assert") { Ast *tag = ast_basic_directive(f, token, name.string); return parse_call_expr(f, tag); + } else if (name.string == "defined") { + Ast *tag = ast_basic_directive(f, token, name.string); + return parse_call_expr(f, tag); } else { operand = ast_tag_expr(f, token, name, parse_expr(f, false)); } diff --git a/src/parser.hpp b/src/parser.hpp index 4f0b4d260..732d499b8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -150,6 +150,7 @@ enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, ProcTag_require_results = 1<<4, + ProcTag_no_context = 1<<6, }; enum ProcCallingConvention {