Skip to content

Commit 9344ba8

Browse files
kpamnanyd-netto
andauthored
Canonicalize names of nested functions by keeping a more fine grained counter -- per (module, method name) pair (JuliaLang#53719) (#179)
As mentioned in JuliaLang#53716, we've been noticing that `precompile` statements lists from one version of our codebase often don't apply cleanly in a slightly different version. That's because a lot of nested and anonymous function names have a global numeric suffix which is incremented every time a new name is generated, and these numeric suffixes are not very stable across codebase changes. To solve this, this PR makes the numeric suffixes a bit more fine grained: every pair of (module, top-level/outermost function name) will have its own counter, which should make nested function names a bit more stable across different versions. This PR applies @JeffBezanson's idea of making the symbol name changes directly in `current-julia-module-counter`. Here is an example: ```Julia julia> function foo(x) function bar(y) return x + y end end foo (generic function with 1 method) julia> f = foo(42) (::var"#bar#foo##0"{Int64}) (generic function with 1 method) ``` Co-authored-by: Diogo Netto <[email protected]>
1 parent 918a99d commit 9344ba8

File tree

8 files changed

+201
-63
lines changed

8 files changed

+201
-63
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
This creates a function taking one argument `x` and returning the value of the polynomial `x^2 +

src/ast.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <stdlib.h>
88
#include <stdio.h>
99
#include <string.h>
10+
1011
#ifdef _OS_WINDOWS_
1112
#include <malloc.h>
1213
#endif
@@ -160,11 +161,46 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint
160161
return (b != NULL && jl_atomic_load_relaxed(&b->owner) == b) ? fl_ctx->T : fl_ctx->F;
161162
}
162163

163-
static value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT
164+
// Used to generate a unique suffix for a given symbol (e.g. variable or type name)
165+
// first argument contains a stack of method definitions seen so far by `closure-convert` in flisp.
166+
// if the top of the stack is non-NIL, we use it to augment the suffix so that it becomes
167+
// of the form $top_level_method_name##$counter, where `counter` is the smallest integer
168+
// such that the resulting name is not already defined in the current module's bindings.
169+
// If the top of the stack is NIL, we simply return the current module's counter.
170+
// This ensures that precompile statements are a bit more stable across different versions
171+
// of a codebase. see #53719
172+
static value_t fl_module_unique_name(fl_context_t *fl_ctx, value_t *args, uint32_t nargs)
164173
{
174+
argcount(fl_ctx, "julia-module-unique-name", nargs, 1);
165175
jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx);
166-
assert(ctx->module);
167-
return fixnum(jl_module_next_counter(ctx->module));
176+
jl_module_t *m = ctx->module;
177+
assert(m != NULL);
178+
// Get the outermost function name from the `parsed_method_stack` top
179+
char *funcname = NULL;
180+
value_t parsed_method_stack = args[0];
181+
if (parsed_method_stack != fl_ctx->NIL) {
182+
value_t bottom_stack_symbol = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "last")), parsed_method_stack);
183+
funcname = tosymbol(fl_ctx, bottom_stack_symbol, "julia-module-unique-name")->name;
184+
}
185+
size_t sz = funcname != NULL ? strlen(funcname) + 32 : 32; // 32 is enough for the suffix
186+
char *buf = (char*)alloca(sz);
187+
if (funcname != NULL && strchr(funcname, '#') == NULL) {
188+
for (int i = 0; ; i++) {
189+
snprintf(buf, sz, "%s##%d", funcname, i);
190+
jl_sym_t *sym = jl_symbol(buf);
191+
JL_LOCK(&m->lock);
192+
if (jl_get_module_binding(m, sym, 0) == NULL) { // make sure this name is not already taken
193+
jl_get_module_binding(m, sym, 1); // create the binding
194+
JL_UNLOCK(&m->lock);
195+
return symbol(fl_ctx, buf);
196+
}
197+
JL_UNLOCK(&m->lock);
198+
}
199+
}
200+
else {
201+
snprintf(buf, sz, "%d", jl_module_next_counter(m));
202+
}
203+
return symbol(fl_ctx, buf);
168204
}
169205

170206
static value_t fl_julia_current_file(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT
@@ -206,7 +242,7 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, jl_module_t *m
206242

207243
static const builtinspec_t julia_flisp_ast_ext[] = {
208244
{ "defined-julia-global", fl_defined_julia_global }, // TODO: can we kill this safepoint
209-
{ "current-julia-module-counter", fl_current_module_counter },
245+
{ "current-julia-module-counter", fl_module_unique_name },
210246
{ "julia-scalar?", fl_julia_scalar },
211247
{ "julia-current-file", fl_julia_current_file },
212248
{ "julia-current-line", fl_julia_current_line },

src/datatype.c

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,21 @@ extern "C" {
1919

2020
// allocating TypeNames -----------------------------------------------------------
2121

22-
static int is10digit(char c) JL_NOTSAFEPOINT
23-
{
24-
return (c >= '0' && c <= '9');
25-
}
26-
2722
static jl_sym_t *jl_demangle_typename(jl_sym_t *s) JL_NOTSAFEPOINT
2823
{
2924
char *n = jl_symbol_name(s);
3025
if (n[0] != '#')
3126
return s;
32-
char *end = strrchr(n, '#');
27+
char *end = strchr(&n[1], '#');
28+
// handle `#f...##...#...`
29+
if (end != NULL && end[1] == '#')
30+
end = strchr(&end[2], '#');
3331
int32_t len;
34-
if (end == n || end == n+1)
32+
if (end == NULL || end == n+1)
3533
len = strlen(n) - 1;
3634
else
3735
len = (end-n) - 1; // extract `f` from `#f#...`
38-
if (is10digit(n[1]))
36+
if (isdigit(n[1]) || is_canonicalized_anonfn_typename(n))
3937
return _jl_symbol(n, len+1);
4038
return _jl_symbol(&n[1], len);
4139
}
@@ -687,14 +685,6 @@ void jl_compute_field_offsets(jl_datatype_t *st)
687685
return;
688686
}
689687

690-
static int is_anonfn_typename(char *name)
691-
{
692-
if (name[0] != '#' || name[1] == '#')
693-
return 0;
694-
char *other = strrchr(name, '#');
695-
return other > &name[1] && is10digit(other[1]);
696-
}
697-
698688
JL_DLLEXPORT jl_datatype_t *jl_new_datatype(
699689
jl_sym_t *name,
700690
jl_module_t *module,

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/gf.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,14 +1537,6 @@ void print_func_loc(JL_STREAM *s, jl_method_t *m)
15371537
}
15381538
}
15391539

1540-
static int is_anonfn_typename(char *name)
1541-
{
1542-
if (name[0] != '#' || name[1] == '#')
1543-
return 0;
1544-
char *other = strrchr(name, '#');
1545-
return other > &name[1] && other[1] > '0' && other[1] <= '9';
1546-
}
1547-
15481540
static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue)
15491541
{
15501542
// method overwritten

0 commit comments

Comments
 (0)