Browse Source

Built-in procedure `#defined`

gingerBill 6 years ago
parent
commit
2ddb27869b
6 changed files with 105 additions and 32 deletions
  1. 14 0
      examples/demo/demo.odin
  2. 56 1
      src/check_expr.cpp
  3. 26 27
      src/ir.cpp
  4. 5 4
      src/ir_print.cpp
  5. 3 0
      src/parser.cpp
  6. 1 0
      src/parser.hpp

+ 14 - 0
examples/demo/demo.odin

@@ -110,6 +110,20 @@ general_stuff :: proc() {
 		My_Struct :: struct{x: int};
 		My_Struct :: struct{x: int};
 		#assert(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");
+		}
+	}
 }
 }
 
 
 
 

+ 56 - 1
src/check_expr.cpp

@@ -2885,6 +2885,36 @@ bool is_type_normal_pointer(Type *ptr, Type **elem) {
 	return false;
 	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) {
 bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) {
 	ast_node(ce, CallExpr, call);
 	ast_node(ce, CallExpr, call);
@@ -2920,6 +2950,15 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_len:
 	case BuiltinProc_len:
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		break;
 		break;
+
+	case BuiltinProc_DIRECTIVE: {
+		ast_node(bd, BasicDirective, ce->proc);
+		String name = bd->name;
+		if (name == "defined") {
+			break;
+		}
+		/*fallthrough*/
+	}
 	default:
 	default:
 		if (ce->args.count > 0) {
 		if (ce->args.count > 0) {
 			check_multi_expr(c, operand, ce->args[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->type = t_untyped_bool;
 			operand->mode = Addressing_Constant;
 			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 {
 		} else {
 			GB_PANIC("Unhandled #%.*s", LIT(name));
 			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) {
 	    ce->proc->kind == Ast_BasicDirective) {
 		ast_node(bd, BasicDirective, ce->proc);
 		ast_node(bd, BasicDirective, ce->proc);
 		String name = bd->name;
 		String name = bd->name;
-		if (name == "location" || name == "assert") {
+		if (name == "location" || name == "assert" || name == "defined") {
 			operand->mode = Addressing_Builtin;
 			operand->mode = Addressing_Builtin;
 			operand->builtin_id = BuiltinProc_DIRECTIVE;
 			operand->builtin_id = BuiltinProc_DIRECTIVE;
 			operand->expr = ce->proc;
 			operand->expr = ce->proc;

+ 26 - 27
src/ir.cpp

@@ -26,7 +26,7 @@ struct irModule {
 	Map<irValue *>        anonymous_proc_lits; // Key: Ast *
 	Map<irValue *>        anonymous_proc_lits; // Key: Ast *
 
 
 	irDebugInfo *         debug_compile_unit;
 	irDebugInfo *         debug_compile_unit;
-	Array<irDebugInfo *>  debug_location_stack; 
+	Array<irDebugInfo *>  debug_location_stack;
 
 
 
 
 	i32                   global_string_index;
 	i32                   global_string_index;
@@ -598,7 +598,7 @@ struct irDebugInfo {
 			irDebugInfo *file;
 			irDebugInfo *file;
 			irDebugInfo *scope;
 			irDebugInfo *scope;
 		} LexicalBlock;
 		} LexicalBlock;
-		
+
 		struct {
 		struct {
 			String          name;
 			String          name;
 			i32             size;
 			i32             size;
@@ -659,6 +659,8 @@ struct irDebugInfo {
 	};
 	};
 };
 };
 
 
+static irDebugInfo IR_DEBUG_INFO_EMPTY = {};
+
 
 
 struct irGen {
 struct irGen {
 	irModule module;
 	irModule module;
@@ -1692,7 +1694,7 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) {
 
 
 	case Basic_i16:
 	case Basic_i16:
 	case Basic_i32:
 	case Basic_i32:
-	case Basic_i64: 
+	case Basic_i64:
 	case Basic_int:
 	case Basic_int:
 	case Basic_rune:
 	case Basic_rune:
 	case Basic_typeid:
 	case Basic_typeid:
@@ -1712,7 +1714,7 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) {
 
 
 	// case Basic_complex32:
 	// case Basic_complex32:
 	case Basic_complex64:
 	case Basic_complex64:
-	case Basic_complex128: 
+	case Basic_complex128:
 	case Basic_cstring:
 	case Basic_cstring:
 	case Basic_string:
 	case Basic_string:
 	case Basic_any:
 	case Basic_any:
@@ -1916,7 +1918,7 @@ irDebugInfo *ir_add_debug_info_type_bit_field(irModule *module, Type *type, Enti
 		                                                         0,
 		                                                         0,
 		                                                         nullptr,
 		                                                         nullptr,
 		                                                         di);
 		                                                         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..
 		// replace with its true bit value here..
 		field_di->DerivedType.size = size;
 		field_di->DerivedType.size = size;
 		field_di->DerivedType.offset = offset; // Offset stored in bits already, no need to convert
 		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);
 			GB_ASSERT(elem_type->Enum.fields.count == base->BitSet.upper + 1);
 		}
 		}
 	}
 	}
-	
+
 	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
 	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
 	di->CompositeType.name = named != nullptr ? named->Named.name : str_lit("bit_set");
 	di->CompositeType.name = named != nullptr ? named->Named.name : str_lit("bit_set");
 	di->CompositeType.tag = irDebugBasicEncoding_structure_type;
 	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);
 		map_set(&module->debug_info, hash_pointer(field_di), field_di);
 		array_add(&elements_di->DebugInfoArray.elements, field_di);
 		array_add(&elements_di->DebugInfoArray.elements, field_di);
 	}
 	}
-	
+
 	return 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.name = type->Basic.name;
 	di->CompositeType.tag = irDebugBasicEncoding_structure_type;
 	di->CompositeType.tag = irDebugBasicEncoding_structure_type;
 	di->CompositeType.size = ir_debug_size_bits(type);
 	di->CompositeType.size = ir_debug_size_bits(type);
-	
+
 	Type *field_type = nullptr;
 	Type *field_type = nullptr;
 	if (type->Basic.kind == Basic_complex64) {
 	if (type->Basic.kind == Basic_complex64) {
 		field_type = t_f32;
 		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)) {
 	if (is_type_map(type)) {
 		// TODO(lachsinc): Looks like "generated_struct_type" map.entries.data is just a u8*, we could
 		// 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).
 		// 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?
 		// 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);
 		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);
 		irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_BasicType);
 		di->BasicType.encoding = irDebugBasicEncoding_unsigned;
 		di->BasicType.encoding = irDebugBasicEncoding_unsigned;
 		// di->BasicType.name = str_lit("todo");
 		// 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);
 		map_set(&module->debug_info, hash_type(type), di);
 		return 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.pos = e->token.pos;
 	di->LocalVariable.arg = arg_id;
 	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 ??
 	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);
 	map_set(&module->debug_info, hash_entity(e), di);
 	return di;
 	return di;
 }
 }
@@ -2561,12 +2563,12 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc) {
 	CheckerInfo *info = proc->module->info;
 	CheckerInfo *info = proc->module->info;
 	String filename = proc->entity->token.pos.file;
 	String filename = proc->entity->token.pos.file;
 	AstFile *f = ast_file_of_filename(info, filename);
 	AstFile *f = ast_file_of_filename(info, filename);
-	irDebugInfo *file = nullptr; 
+	irDebugInfo *file = nullptr;
 	if (f) {
 	if (f) {
 		file = ir_add_debug_info_file(proc->module, f);
 		file = ir_add_debug_info_file(proc->module, f);
 	}
 	}
 	// TODO(lachsinc): Should scope be made separate to file?
 	// TODO(lachsinc): Should scope be made separate to file?
-	irDebugInfo *scope = file; 
+	irDebugInfo *scope = file;
 
 
 	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Proc);
 	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Proc);
 	map_set(&proc->module->debug_info, hash_entity(entity), di);
 	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;
 		return *existing;
 	}
 	}
 	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Location);
 	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;
 	di->Location.scope = scope;
 	map_set(&m->debug_info, hash_node(node), di);
 	map_set(&m->debug_info, hash_node(node), di);
 	return 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<irValue *> args, Ast *expr = nullptr);
-irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array<irValue *> args, Ast *expr = nullptr);
+irValue *ir_emit_runtime_call(irProcedure *proc,                            char const *name_, Array<irValue *> args, Ast *expr = nullptr, ProcInlining inlining = ProcInlining_none);
+irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array<irValue *> args, Ast *expr = nullptr, ProcInlining inlining = ProcInlining_none);
 
 
 
 
 irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) {
 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));
 	args[1] = ir_const_int(type_size_of(t));
 	AstPackage *pkg = get_core_package(p->module->info, str_lit("mem"));
 	AstPackage *pkg = get_core_package(p->module->info, str_lit("mem"));
 	if (p->entity != nullptr && p->entity->token.string != "zero" && p->entity->pkg != pkg) {
 	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));
 	ir_emit(p, ir_instr_zero_init(p, address));
 }
 }
@@ -2803,7 +2806,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 	return result;
 	return result;
 }
 }
 
 
-irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array<irValue *> args, Ast *expr) {
+irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array<irValue *> args, Ast *expr, ProcInlining inlining) {
 	String name = make_string_c(cast(char *)name_);
 	String name = make_string_c(cast(char *)name_);
 
 
 	AstPackage *p = proc->module->info->runtime_package;
 	AstPackage *p = proc->module->info->runtime_package;
@@ -2811,10 +2814,10 @@ irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array<irValu
 	irValue **found = map_get(&proc->module->values, hash_entity(e));
 	irValue **found = map_get(&proc->module->values, hash_entity(e));
 	GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
 	GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
 	irValue *gp = *found;
 	irValue *gp = *found;
-	irValue *call = ir_emit_call(proc, gp, args);
+	irValue *call = ir_emit_call(proc, gp, args, inlining);
 	return call;
 	return call;
 }
 }
-irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array<irValue *> args, Ast *expr) {
+irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array<irValue *> args, Ast *expr, ProcInlining inlining) {
 	String name = make_string_c(cast(char *)name_);
 	String name = make_string_c(cast(char *)name_);
 	String package_name = make_string_c(cast(char *)package_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));
 	irValue **found = map_get(&proc->module->values, hash_entity(e));
 	GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
 	GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
 	irValue *gp = *found;
 	irValue *gp = *found;
-	irValue *call = ir_emit_call(proc, gp, args);
+	irValue *call = ir_emit_call(proc, gp, args, inlining);
 	return call;
 	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);
 				ir_emit_bounds_check(proc, ast_token(ie->index), index, len);
 			}
 			}
 			return ir_addr(elem);
 			return ir_addr(elem);
-			break;
 		}
 		}
 
 
 		case Type_Slice: {
 		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);
 			ir_emit_bounds_check(proc, ast_token(ie->index), index, len);
 			irValue *v = ir_emit_ptr_offset(proc, elem, index);
 			irValue *v = ir_emit_ptr_offset(proc, elem, index);
 			return ir_addr(v);
 			return ir_addr(v);
-			break;
 		}
 		}
 
 
 		case Type_DynamicArray: {
 		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);
 			ir_emit_bounds_check(proc, ast_token(ie->index), index, len);
 			irValue *v = ir_emit_ptr_offset(proc, elem, index);
 			irValue *v = ir_emit_ptr_offset(proc, elem, index);
 			return ir_addr(v);
 			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);
 			ir_emit_bounds_check(proc, ast_token(ie->index), index, len);
 
 
 			return ir_addr(ir_emit_ptr_offset(proc, elem, index));
 			return ir_addr(ir_emit_ptr_offset(proc, elem, index));
-			break;
 		}
 		}
 		}
 		}
 	case_end;
 	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.
 	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
 		// 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.
 		// 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);
 		ir_push_debug_location(proc->module, proc->entity->identifier, proc->debug_scope);
 		GB_ASSERT_NOT_NULL(proc->debug_scope);
 		GB_ASSERT_NOT_NULL(proc->debug_scope);
 	} else {
 	} else {
@@ -8717,7 +8716,7 @@ void ir_build_proc(irValue *value, irProcedure *parent) {
 		proc->module->stmt_state_flags = prev_stmt_state_flags;
 		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.
 	// This may result in debug info being missing for below.
 
 
 	if (proc->type->Proc.has_proc_default_values) {
 	if (proc->type->Proc.has_proc_default_values) {

+ 5 - 4
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);
 	GB_ASSERT(v->kind == irValue_Instr);
 
 
 	if (v->loc != nullptr) {
 	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 {
 	} else {
 		irProcedure *proc = v->Instr.block->proc;
 		irProcedure *proc = v->Instr.block->proc;
 		GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
 		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));
 		irDebugInfo **lookup_di = map_get(&m->debug_info, hash_entity(e));
 		GB_ASSERT_NOT_NULL(*lookup_di);
 		GB_ASSERT_NOT_NULL(*lookup_di);
 		irDebugInfo* local_var_di = *lookup_di;
 		irDebugInfo* local_var_di = *lookup_di;
-		
+
 		ir_write_str_lit(f, "call void @llvm.dbg.declare(");
 		ir_write_str_lit(f, "call void @llvm.dbg.declare(");
 		ir_write_str_lit(f, "metadata ");
 		ir_write_str_lit(f, "metadata ");
 		ir_print_type(f, m, vt);
 		ir_print_type(f, m, vt);

+ 3 - 0
src/parser.cpp

@@ -1677,6 +1677,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		} else if (name.string == "assert") {
 		} else if (name.string == "assert") {
 			Ast *tag = ast_basic_directive(f, token, name.string);
 			Ast *tag = ast_basic_directive(f, token, name.string);
 			return parse_call_expr(f, tag);
 			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 {
 		} else {
 			operand = ast_tag_expr(f, token, name, parse_expr(f, false));
 			operand = ast_tag_expr(f, token, name, parse_expr(f, false));
 		}
 		}

+ 1 - 0
src/parser.hpp

@@ -150,6 +150,7 @@ enum ProcTag {
 	ProcTag_bounds_check    = 1<<0,
 	ProcTag_bounds_check    = 1<<0,
 	ProcTag_no_bounds_check = 1<<1,
 	ProcTag_no_bounds_check = 1<<1,
 	ProcTag_require_results = 1<<4,
 	ProcTag_require_results = 1<<4,
+	ProcTag_no_context      = 1<<6,
 };
 };
 
 
 enum ProcCallingConvention {
 enum ProcCallingConvention {