diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 9a91ea74677500..be81fe529ef7d3 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -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 + diff --git a/src/ast.c b/src/ast.c index 26b95225fbf1c6..7cefa035f46cd0 100644 --- a/src/ast.c +++ b/src/ast.c @@ -7,6 +7,9 @@ #include #include #include + +#include "support/strhash.h" + #ifdef _OS_WINDOWS_ #include #endif @@ -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) diff --git a/src/datatype.c b/src/datatype.c index 1157c1d425cb2f..c78b00fdd22456 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -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); } diff --git a/src/flisp/flisp.h b/src/flisp/flisp.h index 669753a9f5302c..f8dd1cfd81ed0e 100644 --- a/src/flisp/flisp.h +++ b/src/flisp/flisp.h @@ -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); diff --git a/src/init.c b/src/init.c index 86c0877b142890..14090d9273fd97 100644 --- a/src/init.c +++ b/src/init.c @@ -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 diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index a2d3ffdd66f674..6a8fb818728d6e 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -225,6 +225,20 @@ (if lb (list lb ub) (list ub)) (if lb (list lb '(core Any)) '()))))) +;; check if x is of the form (method ) or (method (outerref )) +(define (is-method? x) + (if (and (pair? x) (eq? (car x) 'method)) + (let ((name (cadr x))) + (if (and (pair? name) (eq? (car name) 'outerref)) + (let ((name (cadr name))) + (if (symbol? name) + #t + #f)) + (if (symbol? name) + #t + #f))) + #f)) + (define (method-expr-name m) (let ((name (cadr m))) (cond ((globalref? name) (caddr name)) @@ -372,7 +386,7 @@ (generator (if (expr-contains-p if-generated? body (lambda (x) (not (function-def? x)))) (let* ((gen (generated-version body)) (nongen (non-generated-version body)) - (gname (symbol (string (gensy) "#" (current-julia-module-counter)))) + (gname (symbol (string (gensy) "#" (current-julia-module-counter '())))) (gf (make-generator-function gname names anames gen))) (set! body (insert-after-meta nongen @@ -512,7 +526,7 @@ "" "#") (or und '_) "#" - (string (current-julia-module-counter))))))) + (string (current-julia-module-counter '()))))))) ;; this is a hack: nest these statements inside a call so they get closure ;; converted together, allowing all needed types to be defined before any methods. `(call (core ifelse) (false) (false) (block @@ -1251,7 +1265,7 @@ (list a))) ;; TODO: always use a specific special name like #anon# or _, then ignore ;; this as a local variable name. - (name (symbol (string "#" (current-julia-module-counter))))) + (name (symbol (string "#" (current-julia-module-counter '()))))) (expand-forms `(block (local ,name) (function @@ -3537,9 +3551,9 @@ f(x) = yt(x) (define (clear-capture-bits vinfos) (map vinfo:not-capt vinfos)) -(define (convert-lambda lam fname interp capt-sp opaq) +(define (convert-lambda lam fname interp capt-sp opaq parsed-method-stack) (let ((body (add-box-inits-to-body - lam (cl-convert (cadddr lam) fname lam (table) (table) #f interp opaq (table) (vinfo-to-table (car (lam:vinfo lam))))))) + lam (cl-convert- (cadddr lam) fname lam (table) (table) #f interp opaq parsed-method-stack (table) (vinfo-to-table (car (lam:vinfo lam))))))) `(lambda ,(lam:args lam) (,(clear-capture-bits (car (lam:vinfo lam))) () @@ -3614,7 +3628,7 @@ f(x) = yt(x) ;; declared types. ;; when doing this, the original value needs to be preserved, to ;; ensure the expression `a=b` always returns exactly `b`. -(define (convert-assignment var rhs0 fname lam interp opaq globals locals) +(define (convert-assignment var rhs0 fname lam interp opaq parsed-method-stack globals locals) (cond ((symbol? var) (let* ((vi (get locals var #f)) @@ -3632,7 +3646,7 @@ f(x) = yt(x) (equal? rhs0 '(the_exception))) rhs0 (make-ssavalue))) - (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq (table) locals) #t lam)) + (rhs (convert-for-type-decl rhs1 (cl-convert- vt fname lam #f #f #f interp opaq parsed-method-stack (table) locals) #t lam)) (ex (cond (closed `(call (core setfield!) ,(if interp `($ ,var) @@ -3916,17 +3930,17 @@ f(x) = yt(x) (define (toplevel-preserving? e) (and (pair? e) (memq (car e) '(if elseif block trycatch tryfinally trycatchelse)))) -(define (map-cl-convert exprs fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) +(define (map-cl-convert exprs fname lam namemap defined toplevel interp opaq parsed-method-stack (globals (table)) (locals (table))) (if toplevel (map (lambda (x) - (let ((tl (lift-toplevel (cl-convert x fname lam namemap defined + (let ((tl (lift-toplevel (cl-convert- x fname lam namemap defined (and toplevel (toplevel-preserving? x)) - interp opaq globals locals)))) + interp opaq parsed-method-stack globals locals)))) (if (null? (cdr tl)) (car tl) `(block ,@(cdr tl) ,(car tl))))) exprs) - (map (lambda (x) (cl-convert x fname lam namemap defined #f interp opaq globals locals)) exprs))) + (map (lambda (x) (cl-convert- x fname lam namemap defined #f interp opaq parsed-method-stack globals locals)) exprs))) (define (prepare-lambda! lam) ;; mark all non-arguments as assigned, since locals that are never assigned @@ -3935,11 +3949,17 @@ f(x) = yt(x) (list-tail (car (lam:vinfo lam)) (length (lam:args lam)))) (lambda-optimize-vars! lam)) -(define (cl-convert e fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) +;; must start with a hash and second character must be numeric +(define (anon-function-name? str) + (and (>= (string-length str) 2) + (char=? (string.char str 0) #\#) + (char-numeric? (string.char str 1)))) + +(define (cl-convert-- e fname lam namemap defined toplevel interp opaq parsed-method-stack (globals (table)) (locals (table))) (if (and (not lam) (not (and (pair? e) (memq (car e) '(lambda method macro opaque_closure))))) (if (atom? e) e - (cons (car e) (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals locals))) + (cons (car e) (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) (cond ((symbol? e) (define (new-undef-var name) @@ -3958,7 +3978,7 @@ f(x) = yt(x) (val (if (equal? typ '(core Any)) val `(call (core typeassert) ,val - ,(cl-convert typ fname lam namemap defined toplevel interp opaq globals locals))))) + ,(cl-convert- typ fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))))) `(block ,@(if (eq? box access) '() `((= ,access ,box))) ,undefcheck @@ -3990,8 +4010,8 @@ f(x) = yt(x) e) ((=) (let ((var (cadr e)) - (rhs (cl-convert (caddr e) fname lam namemap defined toplevel interp opaq globals locals))) - (convert-assignment var rhs fname lam interp opaq globals locals))) + (rhs (cl-convert- (caddr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) + (convert-assignment var rhs fname lam interp opaq parsed-method-stack globals locals))) ((local-def) ;; make new Box for local declaration of defined variable (let ((vi (get locals (cadr e) #f))) (if (and vi (vinfo:asgn vi) (vinfo:capt vi)) @@ -4043,7 +4063,7 @@ f(x) = yt(x) cvs))) `(new_opaque_closure ,(cadr e) ,(or (caddr e) '(call (core apply_type) (core Union))) ,(or (cadddr e) '(core Any)) ,allow-partial - (opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs))) + (opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs) parsed-method-stack)) ,@var-exprs)))) ((method) (let* ((name (method-expr-name e)) @@ -4057,7 +4077,7 @@ f(x) = yt(x) (sp-inits (if (or short (not (eq? (car sig) 'block))) '() (map-cl-convert (butlast (cdr sig)) - fname lam namemap defined toplevel interp opaq globals locals))) + fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) (sig (and sig (if (eq? (car sig) 'block) (last sig) sig)))) @@ -4080,26 +4100,26 @@ f(x) = yt(x) ((null? cvs) `(block ,@sp-inits - (method ,(cadr e) ,(cl-convert + (method ,(cadr e) ,(cl-convert- ;; anonymous functions with keyword args generate global ;; functions that refer to the type of a local function (rename-sig-types sig namemap) - fname lam namemap defined toplevel interp opaq globals locals) + fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) ,(let ((body (add-box-inits-to-body lam2 - (cl-convert (cadddr lam2) 'anon lam2 (table) (table) #f interp opaq (table) + (cl-convert- (cadddr lam2) 'anon lam2 (table) (table) #f interp opaq parsed-method-stack (table) (vinfo-to-table (car (lam:vinfo lam2))))))) `(lambda ,(cadr lam2) (,(clear-capture-bits (car vis)) ,@(cdr vis)) ,body))))) (else - (let* ((exprs (lift-toplevel (convert-lambda lam2 '|#anon| #t '() #f))) + (let* ((exprs (lift-toplevel (convert-lambda lam2 '|#anon| #t '() #f parsed-method-stack))) (top-stmts (cdr exprs)) (newlam (compact-and-renumber (linearize (car exprs)) 'none 0))) `(toplevel-butfirst (block ,@sp-inits - (method ,(cadr e) ,(cl-convert sig fname lam namemap defined toplevel interp opaq globals locals) + (method ,(cadr e) ,(cl-convert- sig fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) ,(julia-bq-macro newlam))) ,@top-stmts)))) @@ -4108,9 +4128,11 @@ f(x) = yt(x) (type-name (or (get namemap name #f) (and name (symbol (string (if (= (string.char (string name) 0) #\#) - "" - "#") - name "#" (current-julia-module-counter)))))) + (if (anon-function-name? (string name)) + (string "#" (current-julia-module-counter parsed-method-stack)) + name) + (string "#" name)) + "#" (current-julia-module-counter parsed-method-stack)))))) (alldefs (expr-find-all (lambda (ex) (and (length> ex 2) (eq? (car ex) 'method) (not (eq? ex e)) @@ -4202,12 +4224,12 @@ f(x) = yt(x) (append (map (lambda (gs tvar) (make-assignment gs `(call (core TypeVar) ',tvar (core Any)))) closure-param-syms closure-param-names) - `((method #f ,(cl-convert arg-defs fname lam namemap defined toplevel interp opaq globals locals) + `((method #f ,(cl-convert- arg-defs fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) ,(convert-lambda lam2 (if iskw (caddr (lam:args lam2)) (car (lam:args lam2))) - #f closure-param-names #f))))))) + #f closure-param-names #f parsed-method-stack))))))) (mk-closure ;; expression to make the closure (let* ((var-exprs (map (lambda (v) (let ((cv (assq v (cadr (lam:vinfo lam))))) @@ -4241,7 +4263,7 @@ f(x) = yt(x) (begin (put! defined name #t) `(toplevel-butfirst - ,(convert-assignment name mk-closure fname lam interp opaq globals locals) + ,(convert-assignment name mk-closure fname lam interp opaq parsed-method-stack globals locals) ,@typedef ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits @@ -4255,14 +4277,14 @@ f(x) = yt(x) (table) (table) (null? (cadr e)) ;; only toplevel thunks have 0 args - interp opaq globals (vinfo-to-table (car (lam:vinfo e)))))) + interp opaq parsed-method-stack globals (vinfo-to-table (car (lam:vinfo e)))))) `(lambda ,(cadr e) (,(clear-capture-bits (car (lam:vinfo e))) () ,@(cddr (lam:vinfo e))) (block ,@body)))) ;; remaining `::` expressions are type assertions ((|::|) - (cl-convert `(call (core typeassert) ,@(cdr e)) fname lam namemap defined toplevel interp opaq globals locals)) + (cl-convert- `(call (core typeassert) ,@(cdr e)) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)) ;; remaining `decl` expressions are only type assertions if the ;; argument is global or a non-symbol. ((decl) @@ -4270,7 +4292,7 @@ f(x) = yt(x) (local-in? (cadr e) lam locals)) '(null)) (else - (cl-convert + (cl-convert- (let ((ref (binding-to-globalref (cadr e)))) (if ref (begin @@ -4280,13 +4302,24 @@ f(x) = yt(x) (globaldecl ,ref ,(caddr e)) (null))) `(call (core typeassert) ,@(cdr e)))) - fname lam namemap defined toplevel interp opaq globals locals)))) + fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)))) ;; `with-static-parameters` expressions can be removed now; used only by analyze-vars ((with-static-parameters) - (cl-convert (cadr e) fname lam namemap defined toplevel interp opaq globals locals)) + (cl-convert- (cadr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)) (else (cons (car e) - (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals locals)))))))) + (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)))))))) + +;; wrapper for `cl-convert--` +(define (cl-convert- e fname lam namemap defined toplevel interp opaq parsed-method-stack (globals (table)) (locals (table))) + (if (is-method? e) + (let ((name (method-expr-name e))) + (cl-convert-- e fname lam namemap defined toplevel interp opaq (cons name parsed-method-stack) globals locals)) + (cl-convert-- e fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) + +(define (cl-convert e fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) + (let ((parsed-method-stack '())) + (cl-convert-- e fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) (define (closure-convert e) (cl-convert e #f #f (table) (table) #f #f #f)) diff --git a/src/julia.h b/src/julia.h index efdd6a1b08bf75..d83a835d6e937f 100644 --- a/src/julia.h +++ b/src/julia.h @@ -715,6 +715,7 @@ typedef struct _jl_module_t { int8_t max_methods; jl_mutex_t lock; intptr_t hash; + htable_t *counter_table; } jl_module_t; struct _jl_globalref_t { diff --git a/src/julia_internal.h b/src/julia_internal.h index d9e1a078c8a032..9c9e6890814ed1 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -13,6 +13,7 @@ #include "support/strtod.h" #include "gc-alloc-profiler.h" #include "support/rle.h" +#include #include #include #include @@ -952,7 +953,23 @@ STATIC_INLINE 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'; + return other > &name[1] && isdigit(other[1]); +} + +// Returns true for typenames of anounymous functions that have been canonicalized (i.e. +// we mangled the name of the outermost enclosing function in their name). +STATIC_INLINE int is_canonicalized_anonfn_typename(char *name) JL_NOTSAFEPOINT +{ + if (name[0] != '#') + return 0; + char *delim = strchr(&name[1], '#'); + if (delim == NULL) + return 0; + if (delim[1] != '#') + return 0; + if (!isdigit(delim[2])) + return 0; + return 1; } // Each tuple can exist in one of 4 Vararg states: diff --git a/src/module.c b/src/module.c index 96d94049cff13d..64e105b6c96587 100644 --- a/src/module.c +++ b/src/module.c @@ -66,6 +66,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui jl_module_public(m, name, 1); JL_GC_POP(); } + m->counter_table = NULL; return m; } diff --git a/src/staticdata.c b/src/staticdata.c index 6dfe5e91a9c557..fb194dc4641ce1 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -74,6 +74,8 @@ External links: #include // printf #include // PRIxPTR +#include "support/strhash.h" + #include "julia.h" #include "julia_internal.h" #include "julia_gcext.h" diff --git a/src/support/Makefile b/src/support/Makefile index 1ee98a4eabdeec..a633348475f543 100644 --- a/src/support/Makefile +++ b/src/support/Makefile @@ -8,7 +8,7 @@ JCXXFLAGS += $(CXXFLAGS) JCPPFLAGS += $(CPPFLAGS) JLDFLAGS += $(LDFLAGS) -SRCS := hashing timefuncs ptrhash operators utf8 ios htable bitvector \ +SRCS := hashing timefuncs ptrhash strhash operators utf8 ios htable bitvector \ int2str libsupportinit arraylist strtod rle ifeq ($(OS),WINNT) SRCS += asprintf strptime diff --git a/src/support/strhash.c b/src/support/strhash.c new file mode 100644 index 00000000000000..cbd59bed99d10b --- /dev/null +++ b/src/support/strhash.c @@ -0,0 +1,40 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +/* + pointer hash table + optimized for storing info about particular values +*/ + +#include +#include +#include +#include +#include + +#include "dtypes.h" +#include "hashing.h" +#include "strhash.h" + +#define STR_EQ(x,y) (!strcmp((char*)(x), (char*)(y))) + +#include "htable.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +size_t simple_strhash(size_t key) JL_NOTSAFEPOINT +{ + size_t hash = 0; + const char *str = (const char*)key; + while (*str) { + hash = (hash << 5) - hash + (unsigned char)*str++; + } + return hash; +} + +HTIMPL(strhash, simple_strhash, STR_EQ) + +#ifdef __cplusplus +} +#endif diff --git a/src/support/strhash.h b/src/support/strhash.h new file mode 100644 index 00000000000000..cf45dcadeb4e56 --- /dev/null +++ b/src/support/strhash.h @@ -0,0 +1,18 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#ifndef JL_STRHASH_H +#define JL_STRHASH_H + +#include "htable.h" + +#ifdef __cplusplus +extern "C" { +#endif + +HTPROT(strhash) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/show.jl b/test/show.jl index 63663152d9d91c..65c65111606c52 100644 --- a/test/show.jl +++ b/test/show.jl @@ -755,6 +755,69 @@ end @test startswith(sprint(show, typeof(x->x), context = :module=>@__MODULE__), "var\"") +# PR 53719 +module M53719 + f = x -> x + 1 + function foo(x) + function bar(y) + function baz(z) + return x + y + z + end + return baz + end + return bar + end + function foo2(x) + function bar2(y) + return z -> x + y + z + end + return bar2 + end + lambda1 = (x)->begin + function foo(y) + return x + y + end + return foo + end + lambda2 = (x)->begin + y -> x + y + end +end + +@testset "PR 53719 function names" begin + # M53719.f should be printed as var"#[0-9]+" + @test occursin(r"var\"#[0-9]+", sprint(show, M53719.f, context = :module=>M53719)) + # M53719.foo(1) should be printed as var"#bar" + @test occursin(r"var\"#bar", sprint(show, M53719.foo(1), context = :module=>M53719)) + # M53719.foo(1)(2) should be printed as var"#baz" + @test occursin(r"var\"#baz", sprint(show, M53719.foo(1)(2), context = :module=>M53719)) + # M53719.foo2(1) should be printed as var"#bar2" + @test occursin(r"var\"#bar2", sprint(show, M53719.foo2(1), context = :module=>M53719)) + # M53719.foo2(1)(2) should be printed as var"#foo2##[0-9]+" + @test occursin(r"var\"#foo2##[0-9]+", sprint(show, M53719.foo2(1)(2), context = :module=>M53719)) + # M53719.lambda1(1) should be printed as var"#foo" + @test occursin(r"var\"#foo", sprint(show, M53719.lambda1(1), context = :module=>M53719)) + # M53719.lambda2(1) should be printed as var"#[0-9]+" + @test occursin(r"var\"#[0-9]+", sprint(show, M53719.lambda2(1), context = :module=>M53719)) +end + +@testset "PR 53719 function types" begin + # typeof(M53719.f) should be printed as var"#[0-9]+#[0-9]+" + @test occursin(r"var\"#[0-9]+#[0-9]+", sprint(show, typeof(M53719.f), context = :module=>M53719)) + #typeof(M53719.foo(1)) should be printed as var"#bar#foo##[0-9]+" + @test occursin(r"var\"#bar#foo##[0-9]+", sprint(show, typeof(M53719.foo(1)), context = :module=>M53719)) + #typeof(M53719.foo(1)(2)) should be printed as var"#baz#foo##[0-9]+" + @test occursin(r"var\"#baz#foo##[0-9]+", sprint(show, typeof(M53719.foo(1)(2)), context = :module=>M53719)) + #typeof(M53719.foo2(1)) should be printed as var"#bar2#foo2##[0-9]+" + @test occursin(r"var\"#bar2#foo2##[0-9]+", sprint(show, typeof(M53719.foo2(1)), context = :module=>M53719)) + #typeof(M53719.foo2(1)(2)) should be printed as var"#foo2##[0-9]+#foo2##[0-9]+" + @test occursin(r"var\"#foo2##[0-9]+#foo2##[0-9]+", sprint(show, typeof(M53719.foo2(1)(2)), context = :module=>M53719)) + #typeof(M53719.lambda1(1)) should be printed as var"#foo#[0-9]+" + @test occursin(r"var\"#foo#[0-9]+", sprint(show, typeof(M53719.lambda1(1)), context = :module=>M53719)) + #typeof(M53719.lambda2(1)) should be printed as var"#[0-9]+#[0-9]+" + @test occursin(r"var\"#[0-9]+#[0-9]+", sprint(show, typeof(M53719.lambda2(1)), context = :module=>M53719)) +end + #test methodshow.jl functions @test Base.inbase(Base) @test !Base.inbase(LinearAlgebra)