Skip to content

Commit

Permalink
canonicalize names of nested functions and nested lambdas
Browse files Browse the repository at this point in the history
  • Loading branch information
d-netto committed Sep 5, 2024
1 parent bada969 commit 86e0186
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 50 deletions.
4 changes: 2 additions & 2 deletions doc/src/manual/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,12 @@ syntaxes:

```jldoctest
julia> x -> x^2 + 2x - 1
#1 (generic function with 1 method)
#2 (generic function with 1 method)
julia> function (x)
x^2 + 2x - 1
end
#3 (generic function with 1 method)
#5 (generic function with 1 method)
```

Each statement creates a function taking one argument `x` and returning the value of the polynomial `x^2 +
Expand Down
81 changes: 79 additions & 2 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "support/strhash.h"

#ifdef _OS_WINDOWS_
#include <malloc.h>
#endif
Expand Down Expand Up @@ -215,11 +218,85 @@ static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint
decode_restriction_value(pku) : jl_atomic_load_relaxed(&b->value)) != NULL ? fl_ctx->T : fl_ctx->F;
}

static htable_t *rebuild_counter_table(jl_module_t *m) JL_NOTSAFEPOINT
{
htable_t *counter_table = m->counter_table = htable_new((htable_t *)malloc_s(sizeof(htable_t)), 0);
jl_svec_t *t = jl_atomic_load_relaxed(&m->bindings);
for (size_t i = 0; i < jl_svec_len(t); i++) {
jl_binding_t *b = (jl_binding_t*)jl_svecref(t, i);
if ((void*)b == jl_nothing) {
continue;
}
char *globalref_name = jl_symbol_name(b->globalref->name);
if (is_canonicalized_anonfn_typename(globalref_name)) {
int should_free = 1;
// copy globalref_name into the buffer until we hit a `#` character
char *delim = strchr(&globalref_name[1], '#');
assert(delim != NULL);
size_t len = delim - globalref_name - 1;
assert(len > 0);
char *enclosing_function_name = (char*)calloc_s(len + 1);
memcpy(enclosing_function_name, &globalref_name[1], len);
// check if the enclosing function name is already in the counter table
if (strhash_get(counter_table, enclosing_function_name) == HT_NOTFOUND) {
strhash_put(counter_table, enclosing_function_name, (void*)((uintptr_t)HT_NOTFOUND + 1));
should_free = 0;
}
char *pint = strrchr(globalref_name, '#');
assert(pint != NULL);
int counter = atoi(pint + 1);
int max_seen_so_far = ((uint32_t)(uintptr_t)strhash_get(counter_table, enclosing_function_name) - (uintptr_t)HT_NOTFOUND - 1);
if (counter >= max_seen_so_far) {
strhash_put(counter_table, enclosing_function_name, (void*)((uintptr_t)counter + 1 + (uintptr_t)HT_NOTFOUND + 1));
}
if (should_free) {
free(enclosing_function_name);
}
}
}
return counter_table;
}

// used to generate a unique suffix for a given symbol (e.g. variable or type name)
// first argument contains a stack of method definitions seen so far by `closure-convert` in flisp.
// if the top of the stack is non-NIL, we use it to augment the suffix so that it becomes
// of the form $top_level_method_name##$counter, where counter is stored in a per-module
// side table indexed by top-level method name.
// this ensures that precompile statements are a bit more stable across different versions
// of a codebase. see #53719
static value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT
{
argcount(fl_ctx, "current-julia-module-counter", nargs, 1);
jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx);
assert(ctx->module);
return fixnum(jl_module_next_counter(ctx->module));
jl_module_t *m = ctx->module;
assert(m != NULL);
// Get the outermost function name from the `parsed_method_stack` top
char *funcname = NULL;
value_t parsed_method_stack = args[0];
if (parsed_method_stack != fl_ctx->NIL) {
value_t bottom_stack_symbol = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "last")), parsed_method_stack);
funcname = symbol_name(fl_ctx, bottom_stack_symbol);
}
char buf[(funcname != NULL ? strlen(funcname) : 0) + 20];
if (funcname != NULL && strchr(funcname, '#') == NULL) {
jl_mutex_lock_nogc(&m->lock);
htable_t *counter_table = m->counter_table;
if (counter_table == NULL) {
counter_table = rebuild_counter_table(m);
}
// try to find the function name in the module's counter table, if it's not found, add it
if (strhash_get(counter_table, funcname) == HT_NOTFOUND) {
strhash_put(counter_table, funcname, (void*)((uintptr_t)HT_NOTFOUND + 1));
}
uint32_t nxt = ((uint32_t)(uintptr_t)strhash_get(counter_table, funcname) - (uintptr_t)HT_NOTFOUND - 1);
snprintf(buf, sizeof(buf), "%s##%d", funcname, nxt);
strhash_put(counter_table, funcname, (void*)(nxt + 1 + (uintptr_t)HT_NOTFOUND + 1));
jl_mutex_unlock_nogc(&m->lock);
}
else {
snprintf(buf, sizeof(buf), "%d", jl_module_next_counter(ctx->module));
}
return symbol(fl_ctx, buf);
}

static int jl_is_number(jl_value_t *v)
Expand Down
14 changes: 6 additions & 8 deletions src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,21 @@ extern "C" {

// allocating TypeNames -----------------------------------------------------------

static int is10digit(char c) JL_NOTSAFEPOINT
{
return (c >= '0' && c <= '9');
}

static jl_sym_t *jl_demangle_typename(jl_sym_t *s) JL_NOTSAFEPOINT
{
char *n = jl_symbol_name(s);
if (n[0] != '#')
return s;
char *end = strrchr(n, '#');
char *end = strchr(&n[1], '#');
// handle `#f...##...#...`
if (end != NULL && end[1] == '#')
end = strchr(&end[2], '#');
int32_t len;
if (end == n || end == n+1)
if (end == NULL || end == n+1)
len = strlen(n) - 1;
else
len = (end-n) - 1; // extract `f` from `#f#...`
if (is10digit(n[1]))
if (isdigit(n[1]) || is_canonicalized_anonfn_typename(n))
return _jl_symbol(n, len+1);
return _jl_symbol(&n[1], len);
}
Expand Down
2 changes: 1 addition & 1 deletion src/flisp/flisp.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ value_t fl_cons(fl_context_t *fl_ctx, value_t a, value_t b) JL_NOTSAFEPOINT;
value_t fl_list2(fl_context_t *fl_ctx, value_t a, value_t b) JL_NOTSAFEPOINT;
value_t fl_listn(fl_context_t *fl_ctx, size_t n, ...) JL_NOTSAFEPOINT;
value_t symbol(fl_context_t *fl_ctx, const char *str) JL_NOTSAFEPOINT;
char *symbol_name(fl_context_t *fl_ctx, value_t v);
char *symbol_name(fl_context_t *fl_ctx, value_t v) JL_NOTSAFEPOINT;
int fl_is_keyword_name(const char *str, size_t len);
value_t alloc_vector(fl_context_t *fl_ctx, size_t n, int init);
size_t llength(value_t v);
Expand Down
1 change: 1 addition & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
jl_load(jl_core_module, "boot.jl");
post_boot_hooks();
}
jl_main_module->counter_table = NULL;

if (jl_base_module == NULL) {
// nthreads > 1 requires code in Base
Expand Down
Loading

0 comments on commit 86e0186

Please sign in to comment.