aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2024-06-10 15:02:34 +0100
committergingerBill <bill@gingerbill.org>2024-06-10 15:02:34 +0100
commitfa3cae2bb04db76f52f1b2288a9c858f20332b8a (patch)
tree063118fd6e63aab004680be7a87e1a7670fe4a09
parent1945218f6df814ea95233035d0b51585e2522b2e (diff)
Add `intrinsics.procedure_of`
```odin foo :: proc(x: $T) { fmt.println(x) } bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of` bar(333) // prints 333 ```
-rw-r--r--base/intrinsics/intrinsics.odin4
-rw-r--r--src/check_builtin.cpp46
-rw-r--r--src/check_decl.cpp17
-rw-r--r--src/check_expr.cpp1
-rw-r--r--src/check_stmt.cpp10
-rw-r--r--src/checker.cpp4
-rw-r--r--src/checker.hpp6
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/parser.hpp1
9 files changed, 87 insertions, 6 deletions
diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin
index 8873f3bbc..4f6fa2713 100644
--- a/base/intrinsics/intrinsics.odin
+++ b/base/intrinsics/intrinsics.odin
@@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
// if all listed features are supported.
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
+
+// Returns the value of the procedure where `x` must be a call expression
+procedure_of :: proc(x: $T) -> T where type_is_proc(T) ---
+
// WASM targets only
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
wasm_memory_size :: proc(index: uintptr) -> int ---
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 98c695a2c..3aee804df 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -1843,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_register_class:
case BuiltinProc_atomic_type_is_lock_free:
case BuiltinProc_has_target_feature:
+ case BuiltinProc_procedure_of:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
@@ -6157,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_procedure_of:
+ {
+ Ast *call_expr = unparen_expr(ce->args[0]);
+ Operand op = {};
+ check_expr_base(c, &op, ce->args[0], nullptr);
+ if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) {
+ error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ Ast *proc = call_expr->CallExpr.proc;
+ Entity *e = entity_of_node(proc);
+
+ if (e == nullptr) {
+ error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure");
+ return false;
+ }
+
+ TypeAndValue tav = proc->tav;
+
+
+ operand->type = e->type;
+ operand->mode = Addressing_Value;
+ operand->value = tav.value;
+ operand->builtin_id = BuiltinProc_Invalid;
+ operand->proc_group = nullptr;
+
+ if (tav.mode == Addressing_Builtin) {
+ operand->mode = tav.mode;
+ operand->builtin_id = cast(BuiltinProcId)e->Builtin.id;
+ break;
+ }
+
+ if (!is_type_proc(e->type)) {
+ gbString s = type_to_string(e->type);
+ error(ce->args[0], "Expected a procedure value, got '%s'", s);
+ gb_string_free(s);
+ return false;
+ }
+
+
+ ce->entity_procedure_of = e;
+ break;
+ }
+
case BuiltinProc_constant_utf16_cstring:
{
String value = {};
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 43947836b..13b14149a 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -88,11 +88,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
e->type = t_invalid;
return nullptr;
} else if (is_type_polymorphic(t)) {
- gbString str = type_to_string(t);
- defer (gb_string_free(str));
- error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
- e->type = t_invalid;
- return nullptr;
+ Entity *e = entity_of_node(operand->expr);
+ if (e->state.load() != EntityState_Resolved) {
+ gbString str = type_to_string(t);
+ defer (gb_string_free(str));
+ error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
+ e->type = t_invalid;
+ return nullptr;
+ }
} else if (is_type_empty_union(t)) {
gbString str = type_to_string(t);
defer (gb_string_free(str));
@@ -479,6 +482,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
entity = check_selector(ctx, &operand, init, e->type);
} else {
check_expr_or_type(ctx, &operand, init, e->type);
+ if (init->kind == Ast_CallExpr) {
+ entity = init->CallExpr.entity_procedure_of;
+ }
}
switch (operand.mode) {
@@ -526,6 +532,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
return;
}
+
if (entity != nullptr) {
if (e->type != nullptr) {
Operand x = {};
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 641f70566..01cba881e 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->defer_use_checked = false;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
+ entity->state.store(EntityState_Resolved);
entity->identifier = ident;
add_entity_and_decl_info(&nctx, ident, entity, d);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index c37c58cd6..f2e3b0242 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
}
if (do_require) {
gbString expr_str = expr_to_string(ce->proc);
+ defer (gb_string_free(expr_str));
+ if (builtin_id) {
+ String real_name = builtin_procs[builtin_id].name;
+ if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) {
+ error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str,
+ LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name));
+ return;
+ }
+ }
error(node, "'%s' requires that its results must be handled", expr_str);
- gb_string_free(expr_str);
}
return;
} else if (expr && expr->kind == Ast_SelectorCallExpr) {
diff --git a/src/checker.cpp b/src/checker.cpp
index 8f0cc1cd1..852fb89bb 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1479,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) {
case_ast_node(cc, CaseClause, expr);
return cc->implicit_entity;
case_end;
+
+ case_ast_node(ce, CallExpr, expr);
+ return ce->entity_procedure_of;
+ case_end;
}
return nullptr;
}
diff --git a/src/checker.hpp b/src/checker.hpp
index 2ac4c8e7a..492a64fb6 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -51,6 +51,12 @@ enum StmtFlag {
enum BuiltinProcPkg {
BuiltinProcPkg_builtin,
BuiltinProcPkg_intrinsics,
+ BuiltinProcPkg_COUNT
+};
+
+String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = {
+ str_lit("builtin"),
+ str_lit("intrinsics"),
};
struct BuiltinProc {
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 5f98bb7b3..35acad42f 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end,
+ BuiltinProc_procedure_of,
+
BuiltinProc___entry_point,
BuiltinProc_objc_send,
@@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
diff --git a/src/parser.hpp b/src/parser.hpp
index 0e411d9ac..02f2af28d 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \
bool optional_ok_one; \
bool was_selector; \
AstSplitArgs *split_args; \
+ Entity *entity_procedure_of; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(EnumFieldValue, "enum field value", struct { \