Browse Source

(#594) Add `#config` to replace `#defined`; Restrict `#defined` within procedure bodies to remove race condition

gingerBill 5 years ago
parent
commit
7c42d4ba75
4 changed files with 60 additions and 3 deletions
  1. 47 2
      src/check_expr.cpp
  2. 9 1
      src/checker.cpp
  3. 1 0
      src/checker.hpp
  4. 3 0
      src/parser.cpp

+ 47 - 2
src/check_expr.cpp

@@ -3809,6 +3809,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		if (name == "defined") {
 			break;
 		}
+		if (name == "config") {
+			break;
+		}
 		/*fallthrough*/
 	}
 	default:
@@ -3982,11 +3985,53 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				return false;
 			}
 
+			if (c->curr_proc_decl == nullptr) {
+				error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'");
+				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);
+			operand->value = exact_value_bool(false);
+
+		} else if (name == "config") {
+			if (ce->args.count != 2) {
+				error(call, "'#config' expects 2 argument, got %td", ce->args.count);
+				return false;
+			}
+			Ast *arg = unparen_expr(ce->args[0]);
+			if (arg == nullptr || arg->kind != Ast_Ident) {
+				error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind]));
+				return false;
+			}
+
+			Ast *def_arg = unparen_expr(ce->args[1]);
+
+			Operand def = {};
+			check_expr(c, &def, def_arg);
+			if (def.mode != Addressing_Constant) {
+				error(def_arg, "'#config' default value must be a constant");
+				return false;
+			}
+
+			String name = arg->Ident.token.string;
 
+
+			operand->type = def.type;
+			operand->mode = def.mode;
+			operand->value = def.value;
+
+			Entity *found = scope_lookup_current(config_pkg->scope, name);
+			if (found != nullptr) {
+				if (found->kind != Entity_Constant) {
+					error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name));
+				} else {
+					operand->type = found->type;
+					operand->mode = Addressing_Constant;
+					operand->value = found->Constant.value;
+				}
+			}
 		} else {
 			GB_PANIC("Unhandled #%.*s", LIT(name));
 		}
@@ -7186,7 +7231,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
 	    ce->proc->kind == Ast_BasicDirective) {
 		ast_node(bd, BasicDirective, ce->proc);
 		String name = bd->name;
-		if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "load") {
+		if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") {
 			operand->mode = Addressing_Builtin;
 			operand->builtin_id = BuiltinProc_DIRECTIVE;
 			operand->expr = ce->proc;

+ 9 - 1
src/checker.cpp

@@ -703,6 +703,14 @@ void init_universal(void) {
 	intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
 	intrinsics_pkg->scope->pkg = intrinsics_pkg;
 
+	config_pkg = gb_alloc_item(a, AstPackage);
+	config_pkg->name = str_lit("config");
+	config_pkg->kind = Package_Normal;
+
+	config_pkg->scope = create_scope(nullptr, a);
+	config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
+	config_pkg->scope->pkg = config_pkg;
+
 
 // Types
 	for (isize i = 0; i < gb_count_of(basic_types); i++) {
@@ -783,7 +791,7 @@ void init_universal(void) {
 
 		Entity *entity = alloc_entity_constant(nullptr, make_token_ident(name), type, value);
 		entity->state = EntityState_Resolved;
-		if (scope_insert(builtin_pkg->scope, entity)) {
+		if (scope_insert(config_pkg->scope, entity)) {
 			error(entity->token, "'%s' defined as an argument is already declared at the global scope", name);
 			defined_values_double_declaration = true;
 			// NOTE(bill): Just exit early before anything, even though the compiler will do that anyway

+ 1 - 0
src/checker.hpp

@@ -332,6 +332,7 @@ struct Checker {
 
 gb_global AstPackage *builtin_pkg    = nullptr;
 gb_global AstPackage *intrinsics_pkg = nullptr;
+gb_global AstPackage *config_pkg      = nullptr;
 
 
 HashKey hash_node     (Ast *node)  { return hash_pointer(node); }

+ 3 - 0
src/parser.cpp

@@ -1785,6 +1785,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		} else if (name.string == "defined") {
 			Ast *tag = ast_basic_directive(f, token, name.string);
 			return parse_call_expr(f, tag);
+		} else if (name.string == "config") {
+			Ast *tag = ast_basic_directive(f, token, name.string);
+			return parse_call_expr(f, tag);
 		} else if (name.string == "soa" || name.string == "simd") {
 			Ast *tag = ast_basic_directive(f, token, name.string);
 			Ast *original_type = parse_type(f);