Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

canonicalize names of nested functions and nested lambdas #146

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
```

This creates a function taking one argument `x` and returning the value of the polynomial `x^2 +
Expand Down
57 changes: 55 additions & 2 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,64 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint
return (b != NULL && jl_atomic_load_relaxed(&b->owner) == b) ? fl_ctx->T : fl_ctx->F;
}

// 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 && funcname[0] != '#') {
jl_mutex_lock_nogc(&m->lock);
htable_t *mod_table = m->counter_table;
if (mod_table == NULL) {
mod_table = m->counter_table = htable_new((htable_t *)malloc_s(sizeof(htable_t)), 0);
}
// try to find the function name in the module's counter table, if it's not found, add it
if (ptrhash_get(mod_table, funcname) == HT_NOTFOUND) {
ptrhash_put(mod_table, funcname, (void*)((uintptr_t)HT_NOTFOUND + 1));
}
// counter_table is dropped on serialization, so we need to be conservative
// and check the binding table for potential name collisions
int name_collision_found = 0;
do {
uint32_t nxt = ((uint32_t)(uintptr_t)ptrhash_get(mod_table, funcname) - (uintptr_t)HT_NOTFOUND - 1);
snprintf(buf, sizeof(buf), "%s##%d", funcname, nxt);
ptrhash_put(mod_table, funcname, (void*)(nxt + (uintptr_t)HT_NOTFOUND + 1 + 1));
// Check if the counter is already in use
name_collision_found = 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;
}
if (strstr(jl_symbol_name(b->globalref->name), buf)) {
name_collision_found = 1;
break;
}
}
} while (name_collision_found);
jl_mutex_unlock_nogc(&m->lock);
}
else {
snprintf(buf, sizeof(buf), "%d", jl_module_next_counter(ctx->module));
}
return symbol(fl_ctx, buf);
}

static value_t fl_julia_current_file(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT
Expand Down
32 changes: 21 additions & 11 deletions src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,36 @@ static int is10digit(char c) JL_NOTSAFEPOINT
return (c >= '0' && c <= '9');
}

// Returns true for typenames in the form "#[0-9]+" or "#.*##[0-9]+"
STATIC_INLINE int is_canonicalized_anonfn_typename(char *name) JL_NOTSAFEPOINT
{
if (is10digit(name[1]))
return 1;
char *delim = strchr(&name[1], '#');
if (delim == NULL)
return 0;
if (delim[1] != '#')
return 0;
if (!is10digit(delim[2]))
return 0;
return 1;
}

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 (is_canonicalized_anonfn_typename(n))
return _jl_symbol(n, len+1);
return _jl_symbol(&n[1], len);
}
Expand Down Expand Up @@ -687,14 +705,6 @@ void jl_compute_field_offsets(jl_datatype_t *st)
return;
}

static int is_anonfn_typename(char *name)
{
if (name[0] != '#' || name[1] == '#')
return 0;
char *other = strrchr(name, '#');
return other > &name[1] && is10digit(other[1]);
}

JL_DLLEXPORT jl_datatype_t *jl_new_datatype(
jl_sym_t *name,
jl_module_t *module,
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
8 changes: 0 additions & 8 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1533,14 +1533,6 @@ void print_func_loc(JL_STREAM *s, jl_method_t *m)
}
}

static int is_anonfn_typename(char *name)
{
if (name[0] != '#' || name[1] == '#')
return 0;
char *other = strrchr(name, '#');
return other > &name[1] && other[1] > '0' && other[1] <= '9';
}

static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue)
{
// method overwritten
Expand Down
1 change: 1 addition & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,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
Loading