Skip to content

Commit 1425b44

Browse files
committed
canonicalize names of nested functions and nested lambdas
1 parent 34b81fb commit 1425b44

File tree

14 files changed

+301
-50
lines changed

14 files changed

+301
-50
lines changed

doc/src/manual/functions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,12 @@ syntaxes:
292292

293293
```jldoctest
294294
julia> x -> x^2 + 2x - 1
295-
#1 (generic function with 1 method)
295+
#2 (generic function with 1 method)
296296
297297
julia> function (x)
298298
x^2 + 2x - 1
299299
end
300-
#3 (generic function with 1 method)
300+
#5 (generic function with 1 method)
301301
```
302302

303303
Each statement creates a function taking one argument `x` and returning the value of the polynomial `x^2 +

src/ast.c

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <stdlib.h>
88
#include <stdio.h>
99
#include <string.h>
10+
11+
#include "support/strhash.h"
12+
1013
#ifdef _OS_WINDOWS_
1114
#include <malloc.h>
1215
#endif
@@ -215,11 +218,85 @@ static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint
215218
decode_restriction_value(pku) : jl_atomic_load_relaxed(&b->value)) != NULL ? fl_ctx->T : fl_ctx->F;
216219
}
217220

221+
static htable_t *rebuild_counter_table(jl_module_t *m) JL_NOTSAFEPOINT
222+
{
223+
htable_t *counter_table = m->counter_table = htable_new((htable_t *)malloc_s(sizeof(htable_t)), 0);
224+
jl_svec_t *t = jl_atomic_load_relaxed(&m->bindings);
225+
for (size_t i = 0; i < jl_svec_len(t); i++) {
226+
jl_binding_t *b = (jl_binding_t*)jl_svecref(t, i);
227+
if ((void*)b == jl_nothing) {
228+
continue;
229+
}
230+
char *globalref_name = jl_symbol_name(b->globalref->name);
231+
if (is_canonicalized_anonfn_typename(globalref_name)) {
232+
int should_free = 1;
233+
// copy globalref_name into the buffer until we hit a `#` character
234+
char *delim = strchr(&globalref_name[1], '#');
235+
assert(delim != NULL);
236+
size_t len = delim - globalref_name - 1;
237+
assert(len > 0);
238+
char *enclosing_function_name = (char*)calloc_s(len + 1);
239+
memcpy(enclosing_function_name, &globalref_name[1], len);
240+
// check if the enclosing function name is already in the counter table
241+
if (strhash_get(counter_table, enclosing_function_name) == HT_NOTFOUND) {
242+
strhash_put(counter_table, enclosing_function_name, (void*)((uintptr_t)HT_NOTFOUND + 1));
243+
should_free = 0;
244+
}
245+
char *pint = strrchr(globalref_name, '#');
246+
assert(pint != NULL);
247+
int counter = atoi(pint + 1);
248+
int max_seen_so_far = ((uint32_t)(uintptr_t)strhash_get(counter_table, enclosing_function_name) - (uintptr_t)HT_NOTFOUND - 1);
249+
if (counter >= max_seen_so_far) {
250+
strhash_put(counter_table, enclosing_function_name, (void*)((uintptr_t)counter + 1 + (uintptr_t)HT_NOTFOUND + 1));
251+
}
252+
if (should_free) {
253+
free(enclosing_function_name);
254+
}
255+
}
256+
}
257+
return counter_table;
258+
}
259+
260+
// used to generate a unique suffix for a given symbol (e.g. variable or type name)
261+
// first argument contains a stack of method definitions seen so far by `closure-convert` in flisp.
262+
// if the top of the stack is non-NIL, we use it to augment the suffix so that it becomes
263+
// of the form $top_level_method_name##$counter, where counter is stored in a per-module
264+
// side table indexed by top-level method name.
265+
// this ensures that precompile statements are a bit more stable across different versions
266+
// of a codebase. see #53719
218267
static value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT
219268
{
269+
argcount(fl_ctx, "current-julia-module-counter", nargs, 1);
220270
jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx);
221-
assert(ctx->module);
222-
return fixnum(jl_module_next_counter(ctx->module));
271+
jl_module_t *m = ctx->module;
272+
assert(m != NULL);
273+
// Get the outermost function name from the `parsed_method_stack` top
274+
char *funcname = NULL;
275+
value_t parsed_method_stack = args[0];
276+
if (parsed_method_stack != fl_ctx->NIL) {
277+
value_t bottom_stack_symbol = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "last")), parsed_method_stack);
278+
funcname = symbol_name(fl_ctx, bottom_stack_symbol);
279+
}
280+
char buf[(funcname != NULL ? strlen(funcname) : 0) + 20];
281+
if (funcname != NULL && strchr(funcname, '#') == NULL) {
282+
jl_mutex_lock_nogc(&m->lock);
283+
htable_t *counter_table = m->counter_table;
284+
if (counter_table == NULL) {
285+
counter_table = rebuild_counter_table(m);
286+
}
287+
// try to find the function name in the module's counter table, if it's not found, add it
288+
if (strhash_get(counter_table, funcname) == HT_NOTFOUND) {
289+
strhash_put(counter_table, funcname, (void*)((uintptr_t)HT_NOTFOUND + 1));
290+
}
291+
uint32_t nxt = ((uint32_t)(uintptr_t)strhash_get(counter_table, funcname) - (uintptr_t)HT_NOTFOUND - 1);
292+
snprintf(buf, sizeof(buf), "%s##%d", funcname, nxt);
293+
strhash_put(counter_table, funcname, (void*)(nxt + 1 + (uintptr_t)HT_NOTFOUND + 1));
294+
jl_mutex_unlock_nogc(&m->lock);
295+
}
296+
else {
297+
snprintf(buf, sizeof(buf), "%d", jl_module_next_counter(ctx->module));
298+
}
299+
return symbol(fl_ctx, buf);
223300
}
224301

225302
static int jl_is_number(jl_value_t *v)

src/datatype.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,21 @@ extern "C" {
2020

2121
// allocating TypeNames -----------------------------------------------------------
2222

23-
static int is10digit(char c) JL_NOTSAFEPOINT
24-
{
25-
return (c >= '0' && c <= '9');
26-
}
27-
2823
static jl_sym_t *jl_demangle_typename(jl_sym_t *s) JL_NOTSAFEPOINT
2924
{
3025
char *n = jl_symbol_name(s);
3126
if (n[0] != '#')
3227
return s;
33-
char *end = strrchr(n, '#');
28+
char *end = strchr(&n[1], '#');
29+
// handle `#f...##...#...`
30+
if (end != NULL && end[1] == '#')
31+
end = strchr(&end[2], '#');
3432
int32_t len;
35-
if (end == n || end == n+1)
33+
if (end == NULL || end == n+1)
3634
len = strlen(n) - 1;
3735
else
3836
len = (end-n) - 1; // extract `f` from `#f#...`
39-
if (is10digit(n[1]))
37+
if (isdigit(n[1]) || is_canonicalized_anonfn_typename(n))
4038
return _jl_symbol(n, len+1);
4139
return _jl_symbol(&n[1], len);
4240
}

src/flisp/flisp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ value_t fl_cons(fl_context_t *fl_ctx, value_t a, value_t b) JL_NOTSAFEPOINT;
158158
value_t fl_list2(fl_context_t *fl_ctx, value_t a, value_t b) JL_NOTSAFEPOINT;
159159
value_t fl_listn(fl_context_t *fl_ctx, size_t n, ...) JL_NOTSAFEPOINT;
160160
value_t symbol(fl_context_t *fl_ctx, const char *str) JL_NOTSAFEPOINT;
161-
char *symbol_name(fl_context_t *fl_ctx, value_t v);
161+
char *symbol_name(fl_context_t *fl_ctx, value_t v) JL_NOTSAFEPOINT;
162162
int fl_is_keyword_name(const char *str, size_t len);
163163
value_t alloc_vector(fl_context_t *fl_ctx, size_t n, int init);
164164
size_t llength(value_t v);

src/init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
869869
jl_load(jl_core_module, "boot.jl");
870870
post_boot_hooks();
871871
}
872+
jl_main_module->counter_table = NULL;
872873

873874
if (jl_base_module == NULL) {
874875
// nthreads > 1 requires code in Base

0 commit comments

Comments
 (0)