diff --git a/src/ast.c b/src/ast.c index f18eb2713cc40..3a4b84044eea9 100644 --- a/src/ast.c +++ b/src/ast.c @@ -153,13 +153,26 @@ static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, jl_module_t *mo static value_t julia_to_scm(fl_context_t *fl_ctx, jl_value_t *v); static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, struct macroctx_stack *macroctx, int onelevel, size_t world, int throw_load_error); +static jl_sym_t *scmsym_to_julia(fl_context_t *fl_ctx, value_t s) +{ + assert(issymbol(s)); + if (fl_isgensym(fl_ctx, s)) { + char gsname[16]; + char *n = uint2str(&gsname[1], sizeof(gsname)-1, + ((gensym_t*)ptr(s))->id, 10); + *(--n) = '#'; + return jl_symbol(n); + } + return jl_symbol(symbol_name(fl_ctx, s)); +} + static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) { // tells whether a var is defined in and *by* the current module argcount(fl_ctx, "defined-julia-global", nargs, 1); (void)tosymbol(fl_ctx, args[0], "defined-julia-global"); jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - jl_sym_t *var = jl_symbol(symbol_name(fl_ctx, args[0])); + jl_sym_t *var = scmsym_to_julia(fl_ctx, args[0]); jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); return (b != NULL && jl_atomic_load_relaxed(&b->owner) == b) ? fl_ctx->T : fl_ctx->F; } @@ -167,11 +180,29 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) { // tells whether a var is defined, in the sense that accessing it is nothrow - argcount(fl_ctx, "nothrow-julia-global", nargs, 1); - (void)tosymbol(fl_ctx, args[0], "nothrow-julia-global"); + // can take either a symbol or a module and a symbol jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - jl_sym_t *var = jl_symbol(symbol_name(fl_ctx, args[0])); - jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); + jl_module_t *mod = ctx->module; + jl_sym_t *var = NULL; + if (nargs == 1) { + (void)tosymbol(fl_ctx, args[0], "nothrow-julia-global"); + var = scmsym_to_julia(fl_ctx, args[0]); + } + else { + argcount(fl_ctx, "nothrow-julia-global", nargs, 2); + value_t argmod = args[0]; + if (iscvalue(argmod) && cv_class((cvalue_t*)ptr(argmod)) == jl_ast_ctx(fl_ctx)->jvtype) { + mod = *(jl_module_t**)cv_data((cvalue_t*)ptr(argmod)); + } else { + (void)tosymbol(fl_ctx, argmod, "nothrow-julia-global"); + if (scmsym_to_julia(fl_ctx, argmod) != jl_thismodule_sym) { + lerrorf(fl_ctx, fl_ctx->ArgError, "nothrow-julia-global: Unknown globalref module kind"); + } + } + (void)tosymbol(fl_ctx, args[1], "nothrow-julia-global"); + var = scmsym_to_julia(fl_ctx, args[1]); + } + jl_binding_t *b = jl_get_module_binding(mod, var, 0); b = b ? jl_atomic_load_relaxed(&b->owner) : NULL; return b != NULL && jl_atomic_load_relaxed(&b->value) != NULL ? fl_ctx->T : fl_ctx->F; } @@ -434,20 +465,6 @@ JL_DLLEXPORT void fl_profile(const char *fname) jl_ast_ctx_leave(ctx); } - -static jl_sym_t *scmsym_to_julia(fl_context_t *fl_ctx, value_t s) -{ - assert(issymbol(s)); - if (fl_isgensym(fl_ctx, s)) { - char gsname[16]; - char *n = uint2str(&gsname[1], sizeof(gsname)-1, - ((gensym_t*)ptr(s))->id, 10); - *(--n) = '#'; - return jl_symbol(n); - } - return jl_symbol(symbol_name(fl_ctx, s)); -} - static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, jl_module_t *mod) { jl_value_t *v = NULL; diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 824a8e8e543bb..ceb567f7f2eb9 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4257,6 +4257,7 @@ f(x) = yt(x) (define (valid-ir-argument? e) (or (simple-atom? e) (and (outerref? e) (nothrow-julia-global (cadr e))) + (and (globalref? e) (nothrow-julia-global (cadr e) (caddr e))) (and (pair? e) (memq (car e) '(quote inert top core slot static_parameter))))) @@ -4265,7 +4266,7 @@ f(x) = yt(x) (or (ssavalue? lhs) (valid-ir-argument? e) (and (symbol? lhs) (pair? e) - (memq (car e) '(new splatnew the_exception isdefined call invoke foreigncall cfunction gc_preserve_begin copyast new_opaque_closure))))) + (memq (car e) '(new splatnew the_exception isdefined call invoke foreigncall cfunction gc_preserve_begin copyast new_opaque_closure globalref outerref))))) (define (valid-ir-return? e) ;; returning lambda directly is needed for @generated @@ -4422,6 +4423,13 @@ f(x) = yt(x) (or (memq aval (lam:args lam)) (let ((vi (get vinfo-table aval #f))) (and vi (vinfo:sa vi)))))) + ;; TODO: We could also allow const globals here + (define (const-read-arg? x) + ;; Even if we have side effects, we know that singly-assigned + ;; locals cannot be affected them, so we can inline them anyway. + (or (simple-atom? x) (single-assign-var? x) + (and (pair? x) + (memq (car x) '(quote inert top core))))) ;; evaluate the arguments of a call, creating temporary locations as needed (define (compile-args lst break-labels) (if (null? lst) '() @@ -4442,9 +4450,7 @@ f(x) = yt(x) '(null)))) (loop (cdr lst) (cons (if (and - ;; Even if we have side effects, we know that singly-assigned - ;; locals cannot be affected them, so we can inline them anyway. - (or simple? (single-assign-var? aval)) + (or simple? (const-read-arg? aval)) (valid-body-ir-argument? aval)) aval (let ((tmp (make-ssavalue))) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index ed23da715da69..b057786282973 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5491,6 +5491,11 @@ end @test Base.return_types(phic_type10) |> only === Int @test phic_type10() === 2 +undef_trycatch() = try (a_undef_trycatch = a_undef_trycatch, b = 2); return 1 catch end +# `global a_undef_trycatch` could be defined dynamically, so both paths must be allowed +@test Base.return_types(undef_trycatch) |> only === Union{Nothing, Int} +@test undef_trycatch() === nothing + # Test that `exit` returns `Union{}` (issue #51856) function test_exit_bottom(s) n = tryparse(Int, s) diff --git a/test/core.jl b/test/core.jl index d8eb682bbdfcf..505c5d03a6484 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8048,3 +8048,15 @@ end let lin = Core.LineInfoNode(Base, first(methods(convert)), :foo, Int32(5), Int32(0)) @test convert(LineNumberNode, lin) == LineNumberNode(5, :foo) end + +# Test that a nothrow-globalref doesn't get outlined during lowering +module WellKnownGlobal + global well_known = 1 +end +macro insert_global() + Expr(:call, GlobalRef(Base, :println), GlobalRef(WellKnownGlobal, :well_known)) +end +check_globalref_lowering() = @insert_global +let src = code_lowered(check_globalref_lowering)[1] + @test length(src.code) == 2 +end