diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ab8a7ab63..78566a401 100644 --- a/src/check_expr.cpp +++ b/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; diff --git a/src/checker.cpp b/src/checker.cpp index a35f07b0b..992aeb9b3 100644 --- a/src/checker.cpp +++ b/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 diff --git a/src/checker.hpp b/src/checker.hpp index af45fa249..a8ecddbd3 100644 --- a/src/checker.hpp +++ b/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); } diff --git a/src/parser.cpp b/src/parser.cpp index eb5bdf7b6..ab38951e5 100644 --- a/src/parser.cpp +++ b/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);