diff --git a/base/boot.jl b/base/boot.jl index 5d40191ecab211..626e28aa8b94d0 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -259,18 +259,21 @@ else const UInt = UInt32 end -function iterate end function Typeof end ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, quote (f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x)) end) +function iterate end + macro nospecialize(x) _expr(:meta, :nospecialize, x) end Expr(@nospecialize args...) = _expr(args...) +macro worldinc() Expr(:worldinc) end + _is_internal(__module__) = __module__ === Core # can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping) macro _total_meta() @@ -518,6 +521,7 @@ eval(Core, quote InterConditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) = $(Expr(:new, :InterConditional, :slot, :thentype, :elsetype)) MethodMatch(@nospecialize(spec_types), sparams::SimpleVector, method::Method, fully_covers::Bool) = $(Expr(:new, :MethodMatch, :spec_types, :sparams, :method, :fully_covers)) end) +@worldinc const NullDebugInfo = DebugInfo(:none) diff --git a/base/essentials.jl b/base/essentials.jl index 64fbaea95d4e7b..0550eb62bb632a 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -470,10 +470,20 @@ Evaluate an expression with values interpolated into it using `eval`. If two arguments are provided, the first is the module to evaluate in. """ macro eval(ex) - return Expr(:escape, Expr(:call, GlobalRef(Core, :eval), __module__, Expr(:quote, ex))) + g = ccall(:jl_gensym, Ref{Symbol}, ()) + return Expr(:let, Expr(:(=), g, + Expr(:escape, Expr(:call, GlobalRef(Core, :eval), __module__, Expr(:quote, ex)))), + Expr(:block, + Expr(:var"worldinc-if-toplevel"), + g)) end macro eval(mod, ex) - return Expr(:escape, Expr(:call, GlobalRef(Core, :eval), mod, Expr(:quote, ex))) + g = ccall(:jl_gensym, Ref{Symbol}, ()) + return Expr(:let, Expr(:(=), g, + Expr(:escape, Expr(:call, GlobalRef(Core, :eval), mod, Expr(:quote, ex)))), + Expr(:block, + Expr(:var"worldinc-if-toplevel"), + g)) end # use `@eval` here to directly form `:new` expressions avoid implicit `convert`s diff --git a/base/sysimg.jl b/base/sysimg.jl index ccc8ef38e81bcf..da9b56df47a5bf 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -6,6 +6,7 @@ using .Base # Set up Main module using Base.MainInclude # ans, err, and sometimes Out +Core.@worldinc # These definitions calls Base._include rather than Base.include to get # one-frame stacktraces for the common case of using include(fname) in Main. @@ -29,6 +30,13 @@ actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`i Use [`Base.include`](@ref) to evaluate a file into another module. +!!! note + Julia's syntax lowering recognizes an explicit call to a literal `include` + at top-level and inserts an implicit `@Core.worldinc` to make any include'd + definitions visible to subsequent code. Note however that this recognition + is *syntactic*. I.e. assigning `const myinclude = include` may require + and explicit `@Core.worldinc` call after `myinclude`. + !!! compat "Julia 1.5" Julia 1.5 is required for passing the `mapexpr` argument. """ diff --git a/base/tuple.jl b/base/tuple.jl index fc213410cfd7c6..d9e8739d3b90bf 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -60,7 +60,7 @@ end function _setindex(v, i::Integer, args::Vararg{Any,N}) where {N} @inline - return ntuple(j -> ifelse(j == i, v, args[j]), Val{N}()) + return ntuple(j -> ifelse(j == i, v, args[j]), Val{N}())::NTuple{N, Any} end diff --git a/src/ast.c b/src/ast.c index ea1de429a946ce..51dc6d4680c8c9 100644 --- a/src/ast.c +++ b/src/ast.c @@ -119,6 +119,7 @@ JL_DLLEXPORT jl_sym_t *jl_release_sym; JL_DLLEXPORT jl_sym_t *jl_acquire_release_sym; JL_DLLEXPORT jl_sym_t *jl_sequentially_consistent_sym; JL_DLLEXPORT jl_sym_t *jl_uninferred_sym; +JL_DLLEXPORT jl_sym_t *jl_worldinc_sym; static const uint8_t flisp_system_image[] = { #include @@ -461,6 +462,7 @@ void jl_init_common_symbols(void) jl_acquire_release_sym = jl_symbol("acquire_release"); jl_sequentially_consistent_sym = jl_symbol("sequentially_consistent"); jl_uninferred_sym = jl_symbol("uninferred"); + jl_worldinc_sym = jl_symbol("worldinc"); } JL_DLLEXPORT void jl_lisp_prompt(void) diff --git a/src/interpreter.c b/src/interpreter.c index f9d981687c631b..a85627268894d0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -463,8 +463,6 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, s->ip = ip; if (ip >= ns) jl_error("`body` expression must terminate in `return`. Use `block` instead."); - if (toplevel) - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); jl_value_t *stmt = jl_array_ptr_ref(stmts, ip); assert(!jl_is_phinode(stmt)); size_t next_ip = ip + 1; @@ -643,6 +641,9 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, jl_eval_const_decl(s->module, jl_exprarg(stmt, 0), val); s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing; } + else if (head == jl_worldinc_sym) { + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); + } else if (jl_is_toplevel_only_expr(stmt)) { jl_toplevel_eval(s->module, stmt); } @@ -887,10 +888,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t s->mi = NULL; s->ci = NULL; JL_GC_ENABLEFRAME(s); - jl_task_t *ct = jl_current_task; - size_t last_age = ct->world_age; jl_value_t *r = eval_body(stmts, s, 0, 1); - ct->world_age = last_age; JL_GC_POP(); return r; } diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 808af18ebfdbdf..3d46940d6fcbbc 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -139,7 +139,7 @@ (define (toplevel-only-expr? e) (and (pair? e) - (or (memq (car e) '(toplevel line module import using export public + (or (memq (car e) '(toplevel line module export public error incomplete)) (and (memq (car e) '(global const)) (every symbol? (cdr e)))))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index b48cb48bf0b790..f4c09129e2321a 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1026,6 +1026,7 @@ ;; otherwise do an assignment to trigger an error (const (globalref (thismodule) ,name) ,name))) (const (globalref (thismodule) ,name) ,name)) + (worldinc) (call (core _typebody!) ,name (call (core svec) ,@field-types)) (null))) ;; "inner" constructors @@ -1076,6 +1077,7 @@ (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name)) (null) (const (globalref (thismodule) ,name) ,name)) + (worldinc) (null)))))) (define (primitive-type-def-expr n name params super) @@ -1096,6 +1098,7 @@ (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name)) (null) (const (globalref (thismodule) ,name) ,name)) + (worldinc) (null)))))) ;; take apart a type signature, e.g. T{X} <: S{Y} @@ -1210,7 +1213,7 @@ (cond ((and (length= e 2) (or (symbol? name) (globalref? name))) (if (not (valid-name? name)) (error (string "invalid function name \"" name "\""))) - `(method ,name)) + `(block (method ,name) (worldinc) (unnecessary ,name))) ((not (pair? name)) e) ((eq? (car name) 'call) (let* ((raw-typevars (or where '())) @@ -2733,6 +2736,9 @@ ((and (eq? (identifier-name f) '^) (length= e 4) (integer? (cadddr e))) (expand-forms `(call (top literal_pow) ,f ,(caddr e) (call (call (core apply_type) (top Val) ,(cadddr e)))))) + ((eq? f 'include) + (let ((r (make-ssavalue))) + `(block (= ,r ,(map expand-forms e)) (worldinc-if-toplevel) ,r))) (else (map expand-forms e)))) (map expand-forms e))) @@ -4114,7 +4120,8 @@ f(x) = yt(x) `(lambda ,(cadr lam2) (,(clear-capture-bits (car vis)) ,@(cdr vis)) - ,body))))) + ,body))) + (worldinc))) (else (let* ((exprs (lift-toplevel (convert-lambda lam2 '|#anon| #t '() #f parsed-method-stack))) (top-stmts (cdr exprs)) @@ -4122,7 +4129,8 @@ f(x) = yt(x) `(toplevel-butfirst (block ,@sp-inits (method ,(cadr e) ,(cl-convert sig fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) - ,(julia-bq-macro newlam))) + ,(julia-bq-macro newlam)) + (worldinc)) ,@top-stmts)))) ;; local case - lift to a new type at top level @@ -4261,7 +4269,8 @@ f(x) = yt(x) `(toplevel-butfirst (null) ,@sp-inits - ,@mk-method) + ,@mk-method + (worldinc)) (begin (put! defined name #t) `(toplevel-butfirst @@ -4269,7 +4278,8 @@ f(x) = yt(x) ,@typedef ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits - ,@mk-method)))))))) + ,@mk-method + (worldinc))))))))) ((lambda) ;; happens inside (thunk ...) and generated function bodies (for-each (lambda (vi) (vinfo:set-asgn! vi #t)) (list-tail (car (lam:vinfo e)) (length (lam:args e)))) @@ -4501,6 +4511,7 @@ f(x) = yt(x) ((struct_type) "\"struct\" expression") ((method) "method definition") ((set_binding_type!) (string "type declaration for global \"" (deparse (cadr e)) "\"")) + ((worldinc) "World age increment") (else (string "\"" h "\" expression")))) (if (not (null? (cadr lam))) (error (string (head-to-text (car e)) " not at top level")))) @@ -4952,7 +4963,12 @@ f(x) = yt(x) (else (emit temp))))) ;; top level expressions - ((thunk module) + ((thunk) + (check-top-level e) + (emit e) + (if tail (emit-return tail '(null))) + '(null)) + ((module) (check-top-level e) (emit e) (if tail (emit-return tail '(null))) @@ -4968,8 +4984,22 @@ f(x) = yt(x) (if tail (emit-return tail val)) val)) + ((worldinc-if-toplevel) + (if (null? (cadr lam)) + (emit `(worldinc))) + '(null)) + + ((import using) + (check-top-level e) + (emit e) + (emit `(worldinc)) + (let ((have-ret? (and (pair? code) (pair? (car code)) (eq? (caar code) 'return)))) + (if (and tail (not have-ret?)) + (emit-return tail '(null)))) + '(null)) + ;; other top level expressions - ((import using export public) + ((export public worldinc) (check-top-level e) (emit e) (let ((have-ret? (and (pair? code) (pair? (car code)) (eq? (caar code) 'return)))) diff --git a/src/julia_internal.h b/src/julia_internal.h index 5eb99be9e333f2..31819410b32595 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1841,6 +1841,7 @@ extern JL_DLLEXPORT jl_sym_t *jl_release_sym; extern JL_DLLEXPORT jl_sym_t *jl_acquire_release_sym; extern JL_DLLEXPORT jl_sym_t *jl_sequentially_consistent_sym; extern JL_DLLEXPORT jl_sym_t *jl_uninferred_sym; +extern JL_DLLEXPORT jl_sym_t *jl_worldinc_sym; JL_DLLEXPORT enum jl_memory_order jl_get_atomic_order(jl_sym_t *order, char loading, char storing); JL_DLLEXPORT enum jl_memory_order jl_get_atomic_order_checked(jl_sym_t *order, char loading, char storing); diff --git a/src/toplevel.c b/src/toplevel.c index 45143f99a178c6..0e652481fc1943 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -225,9 +225,7 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex for (int i = 0; i < jl_array_nrows(exprs); i++) { // process toplevel form - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); form = jl_expand_stmt_with_loc(jl_array_ptr_ref(exprs, i), newm, filename, lineno); - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); (void)jl_toplevel_eval_flex(newm, form, 1, 1, &filename, &lineno); } ct->world_age = last_age; @@ -607,7 +605,8 @@ int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT ((jl_expr_t*)e)->head == jl_const_sym || ((jl_expr_t*)e)->head == jl_toplevel_sym || ((jl_expr_t*)e)->head == jl_error_sym || - ((jl_expr_t*)e)->head == jl_incomplete_sym); + ((jl_expr_t*)e)->head == jl_incomplete_sym || + ((jl_expr_t*)e)->head == jl_worldinc_sym); } int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT @@ -864,6 +863,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val if (head == jl_module_sym) { jl_value_t *val = jl_eval_module_expr(m, ex); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); JL_GC_POP(); return val; } @@ -919,6 +919,9 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, "syntax: malformed \"using\" statement"); } + if (!expanded) { + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); + } JL_GC_POP(); return jl_nothing; } @@ -967,6 +970,12 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, "syntax: malformed \"import\" statement"); } + if (!expanded) { + // To avoid having to roundtrip every `using` expression through + // lowering, just to add the world-age increment effect, do it + // manually here. + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); + } JL_GC_POP(); return jl_nothing; } @@ -1111,8 +1120,11 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_in(jl_module_t *m, jl_value_t *ex) jl_value_t *v = NULL; int last_lineno = jl_lineno; const char *last_filename = jl_filename; + jl_task_t *ct = jl_current_task; jl_lineno = 1; jl_filename = "none"; + size_t last_age = ct->world_age; + ct->world_age = jl_atomic_load_relaxed(&jl_world_counter); JL_TRY { v = jl_toplevel_eval(m, ex); } @@ -1123,6 +1135,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_in(jl_module_t *m, jl_value_t *ex) } jl_lineno = last_lineno; jl_filename = last_filename; + ct->world_age = last_age; assert(v); return v; } @@ -1170,6 +1183,7 @@ static jl_value_t *jl_parse_eval_all(jl_module_t *module, jl_value_t *text, int last_lineno = jl_lineno; const char *last_filename = jl_filename; size_t last_age = ct->world_age; + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); int lineno = 0; jl_lineno = 0; const char *filename_str = jl_string_data(filename); @@ -1187,7 +1201,6 @@ static jl_value_t *jl_parse_eval_all(jl_module_t *module, jl_value_t *text, } expression = jl_expand_with_loc_warn(expression, module, jl_string_data(filename), lineno); - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); result = jl_toplevel_eval_flex(module, expression, 1, 1, &filename_str, &lineno); } } diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl index 176860fcdec63f..2fedbde5570787 100644 --- a/stdlib/Logging/test/runtests.jl +++ b/stdlib/Logging/test/runtests.jl @@ -285,7 +285,7 @@ end AboveMaxLevel === Logging.AboveMaxLevel end """) - @test m.run() + @test invokelatest(m.run) end @testset "custom log macro" begin diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index e3a58ec362d89b..04c58c50e608f3 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -367,6 +367,7 @@ function eval_user_input(@nospecialize(ast), backend::REPLBackend, mod::Module) for xf in backend.ast_transforms ast = Base.invokelatest(xf, ast) end + toplevel_eval_with_hooks(mod, Expr(:worldinc)) value = toplevel_eval_with_hooks(mod, ast) backend.in_eval = false setglobal!(Base.MainInclude, :ans, value) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 1355f74c9bfff4..b2595678844864 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -12,150 +12,151 @@ using REPL end end -let ex = quote - module CompletionFoo - using Random - import Test - - mutable struct Test_y - yy - end - mutable struct Test_x - xx :: Test_y - end - type_test = Test_x(Test_y(1)) - (::Test_y)() = "", "" - unicode_αβγ = Test_y(1) +let ex = + quote + module CompletionFoo + using Random + import Test + + mutable struct Test_y + yy + end + mutable struct Test_x + xx :: Test_y + end + type_test = Test_x(Test_y(1)) + (::Test_y)() = "", "" + unicode_αβγ = Test_y(1) - Base.:(+)(x::Test_x, y::Test_y) = Test_x(Test_y(x.xx.yy + y.yy)) - module CompletionFoo2 + Base.:(+)(x::Test_x, y::Test_y) = Test_x(Test_y(x.xx.yy + y.yy)) + module CompletionFoo2 - end - const bar = 1 - foo() = bar - macro foobar() - :() - end - macro barfoo(ex) - ex - end - macro error_expanding() - error("cannot expand @error_expanding") - :() - end - macro error_lowering_conditional(a) - if isa(a, Number) - return a end - throw(AssertionError("Not a Number")) - :() - end - macro error_throwing() - return quote - error("@error_throwing throws an error") + const bar = 1 + foo() = bar + macro foobar() + :() + end + macro barfoo(ex) + ex + end + macro error_expanding() + error("cannot expand @error_expanding") + :() + end + macro error_lowering_conditional(a) + if isa(a, Number) + return a + end + throw(AssertionError("Not a Number")) + :() + end + macro error_throwing() + return quote + error("@error_throwing throws an error") + end end - end - primitive type NonStruct 8 end - Base.propertynames(::NonStruct) = (:a, :b, :c) - x = reinterpret(NonStruct, 0x00) + primitive type NonStruct 8 end + Base.propertynames(::NonStruct) = (:a, :b, :c) + x = reinterpret(NonStruct, 0x00) - # Support non-Dict AbstractDicts, #19441 - mutable struct CustomDict{K, V} <: AbstractDict{K, V} - mydict::Dict{K, V} - end + # Support non-Dict AbstractDicts, #19441 + mutable struct CustomDict{K, V} <: AbstractDict{K, V} + mydict::Dict{K, V} + end - Base.keys(d::CustomDict) = collect(keys(d.mydict)) - Base.length(d::CustomDict) = length(d.mydict) + Base.keys(d::CustomDict) = collect(keys(d.mydict)) + Base.length(d::CustomDict) = length(d.mydict) - # Support AbstractDict with unknown length, #55931 - struct NoLengthDict{K,V} <: AbstractDict{K,V} - dict::Dict{K,V} - NoLengthDict{K,V}() where {K,V} = new(Dict{K,V}()) - end - Base.iterate(d::NoLengthDict, s...) = iterate(d.dict, s...) - Base.IteratorSize(::Type{<:NoLengthDict}) = Base.SizeUnknown() - Base.eltype(::Type{NoLengthDict{K,V}}) where {K,V} = Pair{K,V} - Base.setindex!(d::NoLengthDict, v, k) = d.dict[k] = v - - test(x::T, y::T) where {T<:Real} = pass - test(x::Real, y::Real) = pass - test(x::AbstractArray{T}, y) where {T<:Real} = pass - test(args...) = pass - - test1(x::Type{Float64}) = pass - - test2(x::AbstractString) = pass - test2(x::Char) = pass - test2(x::Cmd) = pass - - test3(x::AbstractArray{Int}, y::Int) = pass - test3(x::AbstractArray{Float64}, y::Float64) = pass - - test4(x::AbstractString, y::AbstractString) = pass - test4(x::AbstractString, y::Regex) = pass - - test5(x::Array{Bool,1}) = pass - test5(x::BitArray{1}) = pass - test5(x::Float64) = pass - const a=x->x - test6()=[a, a] - test7() = rand(Bool) ? 1 : 1.0 - test8() = Any[1][1] - test9(x::Char) = pass - test9(x::Char, i::Int) = pass - - test10(a, x::Int...) = pass - test10(a::Integer, b::Integer, c) = pass - test10(a, y::Bool...) = pass - test10(a, d::Integer, z::Signed...) = pass - test10(s::String...) = pass - - test11(a::Integer, b, c) = pass - test11(u, v::Integer, w) = pass - test11(x::Int, y::Int, z) = pass - test11(_, _, s::String) = pass - - test!12() = pass - - kwtest(; x=1, y=2, w...) = pass - kwtest2(a; x=1, y=2, w...) = pass - kwtest3(a::Number; length, len2, foobar, kwargs...) = pass - kwtest3(a::Real; another!kwarg, len2) = pass - kwtest3(a::Integer; namedarg, foobar, slurp...) = pass - kwtest4(a::AbstractString; _a1b, x23) = pass - kwtest4(a::String; _a1b, xαβγ) = pass - kwtest4(a::SubString; x23, _something) = pass - kwtest5(a::Int, b, x...; somekwarg, somekotherkwarg) = pass - kwtest5(a::Char, b; xyz) = pass - - const named = (; len2=3) - const fmsoebelkv = (; len2=3) - - array = [1, 1] - varfloat = 0.1 - - const tuple = (1, 2) - - test_y_array=[(@__MODULE__).Test_y(rand()) for i in 1:10] - test_dict = Dict("abc"=>1, "abcd"=>10, :bar=>2, :bar2=>9, Base=>3, - occursin=>4, `ls`=>5, 66=>7, 67=>8, ("q",3)=>11, - "α"=>12, :α=>13) - test_customdict = CustomDict(test_dict) - - macro teststr_str(s) end - macro tϵsτstρ_str(s) end - macro testcmd_cmd(s) end - macro tϵsτcmδ_cmd(s) end - - var"complicated symbol with spaces" = 5 - - struct WeirdNames end - Base.propertynames(::WeirdNames) = (Symbol("oh no!"), Symbol("oh yes!")) - - # https://github.com/JuliaLang/julia/issues/52551#issuecomment-1858543413 - export exported_symbol - exported_symbol(::WeirdNames) = nothing + # Support AbstractDict with unknown length, #55931 + struct NoLengthDict{K,V} <: AbstractDict{K,V} + dict::Dict{K,V} + NoLengthDict{K,V}() where {K,V} = new(Dict{K,V}()) + end + Base.iterate(d::NoLengthDict, s...) = iterate(d.dict, s...) + Base.IteratorSize(::Type{<:NoLengthDict}) = Base.SizeUnknown() + Base.eltype(::Type{NoLengthDict{K,V}}) where {K,V} = Pair{K,V} + Base.setindex!(d::NoLengthDict, v, k) = d.dict[k] = v + + test(x::T, y::T) where {T<:Real} = pass + test(x::Real, y::Real) = pass + test(x::AbstractArray{T}, y) where {T<:Real} = pass + test(args...) = pass + + test1(x::Type{Float64}) = pass + + test2(x::AbstractString) = pass + test2(x::Char) = pass + test2(x::Cmd) = pass + + test3(x::AbstractArray{Int}, y::Int) = pass + test3(x::AbstractArray{Float64}, y::Float64) = pass + + test4(x::AbstractString, y::AbstractString) = pass + test4(x::AbstractString, y::Regex) = pass + + test5(x::Array{Bool,1}) = pass + test5(x::BitArray{1}) = pass + test5(x::Float64) = pass + const a=x->x + test6()=[a, a] + test7() = rand(Bool) ? 1 : 1.0 + test8() = Any[1][1] + test9(x::Char) = pass + test9(x::Char, i::Int) = pass + + test10(a, x::Int...) = pass + test10(a::Integer, b::Integer, c) = pass + test10(a, y::Bool...) = pass + test10(a, d::Integer, z::Signed...) = pass + test10(s::String...) = pass + + test11(a::Integer, b, c) = pass + test11(u, v::Integer, w) = pass + test11(x::Int, y::Int, z) = pass + test11(_, _, s::String) = pass + + test!12() = pass + + kwtest(; x=1, y=2, w...) = pass + kwtest2(a; x=1, y=2, w...) = pass + kwtest3(a::Number; length, len2, foobar, kwargs...) = pass + kwtest3(a::Real; another!kwarg, len2) = pass + kwtest3(a::Integer; namedarg, foobar, slurp...) = pass + kwtest4(a::AbstractString; _a1b, x23) = pass + kwtest4(a::String; _a1b, xαβγ) = pass + kwtest4(a::SubString; x23, _something) = pass + kwtest5(a::Int, b, x...; somekwarg, somekotherkwarg) = pass + kwtest5(a::Char, b; xyz) = pass + + const named = (; len2=3) + const fmsoebelkv = (; len2=3) + + array = [1, 1] + varfloat = 0.1 + + const tuple = (1, 2) + + test_y_array=[(@__MODULE__).Test_y(rand()) for i in 1:10] + test_dict = Dict("abc"=>1, "abcd"=>10, :bar=>2, :bar2=>9, Base=>3, + occursin=>4, `ls`=>5, 66=>7, 67=>8, ("q",3)=>11, + "α"=>12, :α=>13) + test_customdict = CustomDict(test_dict) + + macro teststr_str(s) end + macro tϵsτstρ_str(s) end + macro testcmd_cmd(s) end + macro tϵsτcmδ_cmd(s) end + + var"complicated symbol with spaces" = 5 + + struct WeirdNames end + Base.propertynames(::WeirdNames) = (Symbol("oh no!"), Symbol("oh yes!")) + + # https://github.com/JuliaLang/julia/issues/52551#issuecomment-1858543413 + export exported_symbol + exported_symbol(::WeirdNames) = nothing end # module CompletionFoo test_repl_comp_dict = CompletionFoo.test_dict diff --git a/stdlib/Serialization/test/runtests.jl b/stdlib/Serialization/test/runtests.jl index a7d5023e1ec518..c2a14b68be2ed0 100644 --- a/stdlib/Serialization/test/runtests.jl +++ b/stdlib/Serialization/test/runtests.jl @@ -595,7 +595,7 @@ let f_data f_data = "N0pMBwAAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI78jAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzJdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRsEWA1YkH04BBE1haW5EAQEqwCXAFgNWJB9OAQRNYWluRJ0ovyXBFgFVKMAVAAbBAQAAAAEAAAABAAAATsEVRr80EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbMl2/vhW+FcEAAAAVRsGifX5MTExMTsEp" end f = deserialize(IOBuffer(base64decode(f_data))) - @test f(10,3) == 23 + @test invokelatest(f, 10,3) == 23 end # issue #33466, IdDict diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index cf906591b99627..e8c7d49d076aac 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -890,10 +890,17 @@ macro test_warn(msg, expr) quote let fname = tempname() try - ret = open(fname, "w") do f - redirect_stderr(f) do - $(esc(expr)) - end + f = open(fname, "w") + stdold = stderr + redirect_stderr(f) + ret = try + # We deliberately don't use the thunk versions of open/redirect + # to ensure that adding the macro does not change the toplevel-ness + # of the resulting expression. + $(esc(expr)) + finally + redirect_stderr(stdold) + close(f) end @test contains_warn(read(fname, String), $(esc(msg))) ret @@ -922,10 +929,14 @@ macro test_nowarn(expr) # here. let fname = tempname() try - ret = open(fname, "w") do f - redirect_stderr(f) do - $(esc(expr)) - end + f = open(fname, "w") + stdold = stderr + redirect_stderr(f) + ret = try + $(esc(expr)) + finally + redirect_stderr(stdold) + close(f) end stderr_content = read(fname, String) print(stderr, stderr_content) # this is helpful for debugging diff --git a/test/backtrace.jl b/test/backtrace.jl index 68873678df57b1..8b6a65e0a3ed66 100644 --- a/test/backtrace.jl +++ b/test/backtrace.jl @@ -24,6 +24,7 @@ eval(Expr(:function, Expr(:call, :test_inline_1), Expr(:call, :throw, "foo"), Expr(:meta, :pop_loc), Expr(:line, 99))))) +@Core.worldinc @test functionloc(test_inline_1) == ("backtrace.jl", 99) try @@ -49,6 +50,7 @@ eval(Expr(:function, Expr(:call, :test_inline_2), Expr(:call, :throw, "foo"), Expr(:meta, :pop_loc), Expr(:line, 99))))) +@Core.worldinc @test functionloc(test_inline_2) == ("backtrace.jl", 81) try diff --git a/test/ccall.jl b/test/ccall.jl index b10504de21abc3..4872069ecdf310 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -70,8 +70,10 @@ function gen_ccall_echo(x, T, U, ret=nothing) func_ex = :($ret($func_ex)) end @gensym func_name - @eval @noinline $func_name(x) = $func_ex - :($func_name($(esc(x)))) + quote + @noinline $(esc(func_name))(x) = $func_ex + $(esc(func_name))($(esc(x))) + end end macro ccall_echo_func(x, T, U) diff --git a/test/core.jl b/test/core.jl index 4b5a674ba44b3a..a301c6a02df874 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2621,7 +2621,9 @@ end # issue #8338 let ex = Expr(:(=), :(f8338(x;y=4)), :(x*y)) eval(ex) - @test f8338(2) == 8 + invokelatest() do + @test f8338(2) == 8 + end end # call overloading (#2403) @@ -8332,3 +8334,13 @@ let s = mktemp() do path, io end @test strip(s) == "xxx = 42" end + +# `module` has an implicit world-age increment +let foo = eval(Expr(:toplevel, :(module BarModuleInc; struct FooModuleInc; end; end), :(BarModuleInc.FooModuleInc()))) + @Core.worldinc + @test foo == BarModuleInc.FooModuleInc() +end + +eval(:(module BarModuleInc; module BazModuleInc; struct FooModuleInc; end; end; const foo = BazModuleInc.FooModuleInc(); end)) +@Core.worldinc +@test BarModuleInc.foo == BarModuleInc.BazModuleInc.FooModuleInc() diff --git a/test/deprecation_exec.jl b/test/deprecation_exec.jl index 61ffcc2a59ac6e..92fb3d98cb015b 100644 --- a/test/deprecation_exec.jl +++ b/test/deprecation_exec.jl @@ -68,6 +68,7 @@ begin # @deprecate ex = :(module M22845; import ..DeprecationTests: bar; bar(x::Number) = x + 3; end) @test_warn "importing deprecated binding" eval(ex) + @Core.worldinc @test @test_nowarn(DeprecationTests.bar(4)) == 7 @test @test_warn "`f1` is deprecated, use `f` instead." f1() diff --git a/test/error.jl b/test/error.jl index 8657c707207792..550d7ae634e120 100644 --- a/test/error.jl +++ b/test/error.jl @@ -93,6 +93,7 @@ end @testset "MethodError for methods without line numbers" begin try eval(Expr(:function, :(f44319()), 0)) + @Core.worldinc f44319(1) catch e s = sprint(showerror, e) diff --git a/test/llvmcall.jl b/test/llvmcall.jl index c83ac05b1ec481..cb87ae3386597c 100644 --- a/test/llvmcall.jl +++ b/test/llvmcall.jl @@ -158,6 +158,7 @@ module ObjLoadTest end @test_throws(ErrorException("@ccallable was already defined for this method name"), @eval @ccallable Cvoid jl_the_callback(not_the_method::Int) = "other") + @Core.worldinc # Make sure everything up until here gets compiled @test jl_the_callback() === nothing @test jl_the_callback(1) == "other" diff --git a/test/rebinding.jl b/test/rebinding.jl index c93c34be7a75c6..0396bbb1fb4248 100644 --- a/test/rebinding.jl +++ b/test/rebinding.jl @@ -15,6 +15,7 @@ module Rebinding @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_CONST @test !contains(repr(x), "@world") Base.delete_binding(@__MODULE__, :Foo) + @Core.worldinc @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_GUARD @test contains(repr(x), "@world") diff --git a/test/reflection.jl b/test/reflection.jl index 8c701acb9c09de..0c2c330a598732 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -777,6 +777,7 @@ faz1(x) = foo1(x) @test faz1(1.0) == 1 m = first(methods(bar1, Tuple{Int})) Base.delete_method(m) +@Core.worldinc @test bar1(1) == 1 @test bar1(1.0) == 1 @test foo1(1) == 1 @@ -793,6 +794,7 @@ faz2(x) = foo2(x) @test foo2(1.0) == 1 m = first(methods(bar2, Tuple{Int})) Base.delete_method(m) +@Core.worldinc @test bar2(1.0) == 1 @test bar2(1) == 1 @test foo2(1) == 1 @@ -809,6 +811,7 @@ faz3(x) = foo3(x) @test bar3(1.0) == 1 m = first(methods(bar3, Tuple{Int})) Base.delete_method(m) +@Core.worldinc @test bar3(1) == 1 @test bar3(1.0) == 1 @test foo3(1) == 1 @@ -823,6 +826,7 @@ foo4(x) = bar4(x) faz4(x) = foo4(x) m = first(methods(bar4, Tuple{Int})) Base.delete_method(m) +@Core.worldinc @test bar4(1) == 1 @test bar4(1.0) == 1 @test foo4(1) == 1 @@ -846,12 +850,15 @@ log = Ref{String}() @test f48802!(log, 1) == 2 @test log[] == "default" addmethod_48802() +@Core.worldinc @test f48802!(log, 1) == 2 @test log[] == "specialized" Base.delete_method(which(f48802!, Tuple{Any, Int})) +@Core.worldinc @test f48802!(log, 1) == 2 @test log[] == "default" addmethod_48802() +@Core.worldinc @test f48802!(log, 1) == 2 @test log[] == "specialized" @@ -862,6 +869,7 @@ fookw(y::Int) = 2 @test fookw(1) == 2 m = collect(methods(fookw))[2] Base.delete_method(m) +@Core.worldinc @test fookw(1) == 2 @test_throws MethodError fookw("string") @@ -873,12 +881,14 @@ end @test foomany(Int32(5), "hello", 3.2) == "hello" m = first(methods(foomany, Tuple{Int32, String, Float64})) Base.delete_method(m) +@Core.worldinc @test_throws MethodError foomany(Int32(5), "hello", 3.2) struct EmptyType end Base.convert(::Type{EmptyType}, x::Integer) = EmptyType() m = first(methods(convert, Tuple{Type{EmptyType}, Integer})) Base.delete_method(m) +@Core.worldinc @test_throws MethodError convert(EmptyType, 1) # parametric methods @@ -886,6 +896,7 @@ parametric(A::Array{T,N}, i::Vararg{Int,N}) where {T,N} = N @test parametric(rand(2,2), 1, 1) == 2 m = first(methods(parametric)) Base.delete_method(m) +@Core.worldinc @test_throws MethodError parametric(rand(2,2), 1, 1) # Deletion and ambiguity detection @@ -893,10 +904,12 @@ foo(::Int, ::Int) = 1 foo(::Real, ::Int) = 2 foo(::Int, ::Real) = 3 Base.delete_method(first(methods(foo))) +@Core.worldinc @test_throws MethodError foo(1, 1) foo(::Int, ::Int) = 1 foo(1, 1) Base.delete_method(first(methods(foo))) +@Core.worldinc @test_throws MethodError foo(1, 1) # multiple deletions and ambiguities @@ -918,6 +931,7 @@ uambig(::Union{Float64,Nothing}) = 2 @test_throws MethodError uambig(nothing) m = which(uambig, Tuple{Int}) Base.delete_method(m) +@Core.worldinc @test_throws MethodError uambig(1) @test uambig(1.0) == 2 @test uambig(nothing) == 2 diff --git a/test/syntax.jl b/test/syntax.jl index c19721b5c54b3a..9309d42f5bceb8 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -553,7 +553,14 @@ for (str, tag) in Dict("" => :none, "\"" => :string, "#=" => :comment, "'" => :c end # meta nodes for optional positional arguments -let src = Meta.lower(Main, :(@inline f(p::Int=2) = 3)).args[1].code[end-2].args[3] +let code = Meta.lower(Main, :(@inline f(p::Int=2) = 3)).args[1].code + local src + for i = length(code):-1:1 + if Meta.isexpr(code[i], :method) + src = code[i].args[3] + break + end + end @test Core.Compiler.is_declared_inline(src) end @@ -578,6 +585,7 @@ let thismodule = @__MODULE__, @test isa(ex, Expr) @test !isdefined(M16096, :foo16096) local_foo16096 = Core.eval(@__MODULE__, ex) + Core.@worldinc @test local_foo16096(2.0) == 1 @test !@isdefined foo16096 @test !@isdefined it @@ -3102,6 +3110,7 @@ end ex = Expr(:block) ex.args = fill!(Vector{Any}(undef, 700000), 1) f = eval(Expr(:function, :(), ex)) + @Core.worldinc @test f() == 1 ex = Expr(:vcat) ex.args = fill!(Vector{Any}(undef, 600000), 1)