Skip to content

Commit

Permalink
Merge pull request #4242 from laytan/caller-expression
Browse files Browse the repository at this point in the history
add '#caller_expression'
  • Loading branch information
gingerBill authored Sep 16, 2024
2 parents 68619f2 + 603efa8 commit a16d3b6
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 13 deletions.
4 changes: 2 additions & 2 deletions base/runtime/core_builtin.odin
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions core/odin/parser/parser.odin
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions core/testing/testing.odin
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
16 changes: 16 additions & 0 deletions src/check_builtin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion src/check_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
32 changes: 32 additions & 0 deletions src/check_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ enum ParameterValueKind {
ParameterValue_Constant,
ParameterValue_Nil,
ParameterValue_Location,
ParameterValue_Expression,
ParameterValue_Value,
};

Expand Down
2 changes: 1 addition & 1 deletion src/llvm_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &param_value, TokenPos const &pos);
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_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);
Expand Down
62 changes: 57 additions & 5 deletions src/llvm_backend_proc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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 &param_value, TokenPos const &pos) {
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast* call_expression) {
switch (param_value.kind) {
case ParameterValue_Constant:
if (is_type_constant_type(parameter_type)) {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand All @@ -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:
Expand Down

0 comments on commit a16d3b6

Please sign in to comment.