mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-28 01:03:56 +00:00
Merge pull request #4242 from laytan/caller-expression
add '#caller_expression'
This commit is contained in:
@@ -913,7 +913,7 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
||||
assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
if !condition {
|
||||
// NOTE(bill): This is wrapped in a procedure call
|
||||
// to improve performance to make the CPU not
|
||||
@@ -952,7 +952,7 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert_contextless :: proc "contextless" (condition: bool, message := "", loc := #caller_location) {
|
||||
assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
if !condition {
|
||||
// NOTE(bill): This is wrapped in a procedure call
|
||||
// to improve performance to make the CPU not
|
||||
|
||||
@@ -2277,6 +2277,16 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
bd.name = name.text
|
||||
return bd
|
||||
|
||||
case "caller_expression":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
|
||||
if peek_token_kind(p, .Open_Paren) {
|
||||
return parse_call_expr(p, bd)
|
||||
}
|
||||
return bd
|
||||
|
||||
case "location", "exists", "load", "load_directory", "load_hash", "hash", "assert", "panic", "defined", "config":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
|
||||
@@ -105,9 +105,13 @@ cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
|
||||
append(&t.cleanups, Internal_Cleanup{procedure, user_data, context})
|
||||
}
|
||||
|
||||
expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool {
|
||||
expect :: proc(t: ^T, ok: bool, msg := "", expr := #caller_expression(ok), loc := #caller_location) -> bool {
|
||||
if !ok {
|
||||
log.error(msg, location=loc)
|
||||
if msg == "" {
|
||||
log.errorf("expected %v to be true", expr, location=loc)
|
||||
} else {
|
||||
log.error(msg, location=loc)
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
@@ -119,10 +123,10 @@ expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_loc
|
||||
return ok
|
||||
}
|
||||
|
||||
expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
|
||||
expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location, value_expr := #caller_expression(value)) -> bool where intrinsics.type_is_comparable(T) {
|
||||
ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
|
||||
if !ok {
|
||||
log.errorf("expected %v, got %v", expected, value, location=loc)
|
||||
log.errorf("expected %v to be %v, got %v", value_expr, expected, value, location=loc)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -1632,6 +1632,22 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
|
||||
operand->type = t_source_code_location;
|
||||
operand->mode = Addressing_Value;
|
||||
} else if (name == "caller_expression") {
|
||||
if (ce->args.count > 1) {
|
||||
error(ce->args[0], "'#caller_expression' expects either 0 or 1 arguments, got %td", ce->args.count);
|
||||
}
|
||||
if (ce->args.count > 0) {
|
||||
Ast *arg = ce->args[0];
|
||||
Operand o = {};
|
||||
Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
|
||||
if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
|
||||
error(ce->args[0], "'#caller_expression' expected a valid earlier parameter name");
|
||||
}
|
||||
arg->Ident.entity = e;
|
||||
}
|
||||
|
||||
operand->type = t_string;
|
||||
operand->mode = Addressing_Value;
|
||||
} else if (name == "exists") {
|
||||
if (ce->args.count != 1) {
|
||||
error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count);
|
||||
|
||||
@@ -7807,7 +7807,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
name == "load" ||
|
||||
name == "load_directory" ||
|
||||
name == "load_hash" ||
|
||||
name == "hash"
|
||||
name == "hash" ||
|
||||
name == "caller_expression"
|
||||
) {
|
||||
operand->mode = Addressing_Builtin;
|
||||
operand->builtin_id = BuiltinProc_DIRECTIVE;
|
||||
@@ -8725,6 +8726,10 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
|
||||
error(node, "#caller_location may only be used as a default argument parameter");
|
||||
o->type = t_source_code_location;
|
||||
o->mode = Addressing_Value;
|
||||
} else if (name == "caller_expression") {
|
||||
error(node, "#caller_expression may only be used as a default argument parameter");
|
||||
o->type = t_string;
|
||||
o->mode = Addressing_Value;
|
||||
} else {
|
||||
if (name == "location") {
|
||||
init_core_source_code_location(c->checker);
|
||||
|
||||
@@ -1605,6 +1605,25 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool is_caller_expression(Ast *expr) {
|
||||
if (expr->kind == Ast_BasicDirective && expr->BasicDirective.name.string == "caller_expression") {
|
||||
return true;
|
||||
}
|
||||
|
||||
Ast *call = unparen_expr(expr);
|
||||
if (call->kind != Ast_CallExpr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ast_node(ce, CallExpr, call);
|
||||
if (ce->proc->kind != Ast_BasicDirective) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String name = bd->name.string;
|
||||
return name == "caller_expression";
|
||||
}
|
||||
|
||||
gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) {
|
||||
ParameterValue param_value = {};
|
||||
@@ -1626,7 +1645,19 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
|
||||
if (in_type) {
|
||||
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
|
||||
}
|
||||
} else if (is_caller_expression(expr)) {
|
||||
if (expr->kind != Ast_BasicDirective) {
|
||||
check_builtin_procedure_directive(ctx, &o, expr, t_string);
|
||||
}
|
||||
|
||||
param_value.kind = ParameterValue_Expression;
|
||||
o.type = t_string;
|
||||
o.mode = Addressing_Value;
|
||||
o.expr = expr;
|
||||
|
||||
if (in_type) {
|
||||
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
|
||||
}
|
||||
} else {
|
||||
if (in_type) {
|
||||
check_expr_with_type_hint(ctx, &o, expr, in_type);
|
||||
@@ -1858,6 +1889,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
case ParameterValue_Nil:
|
||||
break;
|
||||
case ParameterValue_Location:
|
||||
case ParameterValue_Expression:
|
||||
case ParameterValue_Value:
|
||||
gbString str = type_to_string(type);
|
||||
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
|
||||
|
||||
@@ -104,6 +104,7 @@ enum ParameterValueKind {
|
||||
ParameterValue_Constant,
|
||||
ParameterValue_Nil,
|
||||
ParameterValue_Location,
|
||||
ParameterValue_Expression,
|
||||
ParameterValue_Value,
|
||||
};
|
||||
|
||||
|
||||
@@ -528,7 +528,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
|
||||
gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
|
||||
gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos);
|
||||
|
||||
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos);
|
||||
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TypeProc *procedure_type, Ast *call_expression);
|
||||
|
||||
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
|
||||
gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
|
||||
|
||||
@@ -699,7 +699,9 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
|
||||
}
|
||||
|
||||
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
|
||||
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
|
||||
GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Location);
|
||||
GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Expression);
|
||||
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, nullptr, nullptr);
|
||||
lb_addr_store(p, res, c);
|
||||
}
|
||||
|
||||
@@ -3420,7 +3422,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos) {
|
||||
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TypeProc *procedure_type, Ast* call_expression) {
|
||||
switch (param_value.kind) {
|
||||
case ParameterValue_Constant:
|
||||
if (is_type_constant_type(parameter_type)) {
|
||||
@@ -3446,8 +3448,60 @@ gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type,
|
||||
if (p->entity != nullptr) {
|
||||
proc_name = p->entity->token.string;
|
||||
}
|
||||
|
||||
ast_node(ce, CallExpr, call_expression);
|
||||
TokenPos pos = ast_token(ce->proc).pos;
|
||||
|
||||
return lb_emit_source_code_location_as_global(p, proc_name, pos);
|
||||
}
|
||||
case ParameterValue_Expression:
|
||||
{
|
||||
Ast *orig = param_value.original_ast_expr;
|
||||
if (orig->kind == Ast_BasicDirective) {
|
||||
gbString expr = expr_to_string(call_expression, temporary_allocator());
|
||||
return lb_const_string(p->module, make_string_c(expr));
|
||||
}
|
||||
|
||||
isize param_idx = -1;
|
||||
String param_str = {0};
|
||||
{
|
||||
Ast *call = unparen_expr(orig);
|
||||
GB_ASSERT(call->kind == Ast_CallExpr);
|
||||
ast_node(ce, CallExpr, call);
|
||||
GB_ASSERT(ce->proc->kind == Ast_BasicDirective);
|
||||
GB_ASSERT(ce->args.count == 1);
|
||||
Ast *target = ce->args[0];
|
||||
GB_ASSERT(target->kind == Ast_Ident);
|
||||
String target_str = target->Ident.token.string;
|
||||
|
||||
param_idx = lookup_procedure_parameter(procedure_type, target_str);
|
||||
param_str = target_str;
|
||||
}
|
||||
GB_ASSERT(param_idx >= 0);
|
||||
|
||||
|
||||
Ast *target_expr = nullptr;
|
||||
ast_node(ce, CallExpr, call_expression);
|
||||
|
||||
if (ce->split_args->positional.count > param_idx) {
|
||||
target_expr = ce->split_args->positional[param_idx];
|
||||
}
|
||||
|
||||
for_array(i, ce->split_args->named) {
|
||||
Ast *arg = ce->split_args->named[i];
|
||||
ast_node(fv, FieldValue, arg);
|
||||
GB_ASSERT(fv->field->kind == Ast_Ident);
|
||||
String name = fv->field->Ident.token.string;
|
||||
if (name == param_str) {
|
||||
target_expr = fv->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gbString expr = expr_to_string(target_expr, temporary_allocator());
|
||||
return lb_const_string(p->module, make_string_c(expr));
|
||||
}
|
||||
|
||||
case ParameterValue_Value:
|
||||
return lb_build_expr(p, param_value.ast_value);
|
||||
}
|
||||
@@ -3739,8 +3793,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
}
|
||||
|
||||
TokenPos pos = ast_token(ce->proc).pos;
|
||||
|
||||
|
||||
if (pt->params != nullptr) {
|
||||
isize min_count = pt->params->Tuple.variables.count;
|
||||
@@ -3764,7 +3816,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
args[arg_index] = lb_const_nil(p->module, e->type);
|
||||
break;
|
||||
case Entity_Variable:
|
||||
args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos);
|
||||
args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pt, expr);
|
||||
break;
|
||||
|
||||
case Entity_Constant:
|
||||
|
||||
Reference in New Issue
Block a user