From dc3953db43b3c4c8e9c13cb92a7a4c4690d03325 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Mar 2023 14:56:41 +0100 Subject: [PATCH] Reland: expose caller world to GeneratedFunctionStub (#48766) Expose the demanded world to the GeneratedFunctionStub caller, for users such as Cassette. If this argument is used, the user must return a CodeInfo with the min/max world field set correctly. Make the internal representation a tiny bit more compact also, removing a little bit of unnecessary metadata. Remove support for returning `body isa CodeInfo` via this wrapper, since it is impossible to return a correct object via the GeneratedFunctionStub since it strips off the world argument, which is required for it to do so. This also removes support for not inferring these fully (expand_early=false). Also answer method lookup queries about the future correctly, by refusing to answer them. This helps keeps execution correct as methods get added to the system asynchronously. This reverts "fix #25678: return matters for generated functions (#40778)" (commit 92c84bf3865403355af463b5a1dee42bf7143592), since this is no longer sensible to return here anyways, so it is no longer permitted or supported by this macro. Fixes various issues where we failed to specify the correct world. Note that the passed world may be `typemax(UInt)`, which demands that the generator must return code that is unspecialized (guaranteed to run correctly in any world). Co-authored-by: Jameson Nash --- base/Base.jl | 2 +- base/boot.jl | 27 ++++----- base/compiler/abstractinterpretation.jl | 13 +++-- base/compiler/bootstrap.jl | 2 +- base/compiler/inferencestate.jl | 3 +- base/compiler/optimize.jl | 3 +- base/compiler/typeinfer.jl | 2 +- base/compiler/types.jl | 2 +- base/compiler/utilities.jl | 8 +-- base/compiler/validation.jl | 7 +-- base/expr.jl | 5 +- base/reflection.jl | 39 ++++++++----- doc/src/devdocs/ast.md | 8 +-- src/aotcompile.cpp | 2 +- src/ast.c | 4 +- src/gf.c | 7 ++- src/interpreter.c | 8 ++- src/jitlayers.cpp | 4 +- src/julia-syntax.scm | 9 +-- src/julia.h | 2 +- src/julia_internal.h | 2 +- src/method.c | 47 +++++++--------- test/ambiguous.jl | 6 +- test/compiler/contextual.jl | 33 +++++------ test/compiler/inference.jl | 74 +++++++++++++------------ test/compiler/validation.jl | 9 ++- test/core.jl | 3 +- test/opaque_closure.jl | 31 +++++------ test/reflection.jl | 2 +- test/staged.jl | 40 ++++++++----- test/syntax.jl | 3 - 31 files changed, 204 insertions(+), 203 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 789fb7c0da05c..5e3e64a104ee6 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -479,7 +479,7 @@ in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules for match = _methods(+, (Int, Int), -1, get_world_counter()) m = match.method delete!(push!(Set{Method}(), m), m) - copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match))) + copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match), typemax(UInt))) empty!(Set()) push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) diff --git a/base/boot.jl b/base/boot.jl index b4e01b0c884c1..ca6e6c81405e2 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -590,28 +590,25 @@ println(@nospecialize a...) = println(stdout, a...) struct GeneratedFunctionStub gen - argnames::Array{Any,1} - spnames::Union{Nothing, Array{Any,1}} - line::Int - file::Symbol - expand_early::Bool + argnames::SimpleVector + spnames::SimpleVector end -# invoke and wrap the results of @generated -function (g::GeneratedFunctionStub)(@nospecialize args...) +# invoke and wrap the results of @generated expression +function (g::GeneratedFunctionStub)(world::UInt, source::LineNumberNode, @nospecialize args...) + # args is (spvals..., argtypes...) body = g.gen(args...) - if body isa CodeInfo - return body - end - lam = Expr(:lambda, g.argnames, - Expr(Symbol("scope-block"), + file = source.file + file isa Symbol || (file = :none) + lam = Expr(:lambda, Expr(:argnames, g.argnames...).args, + Expr(:var"scope-block", Expr(:block, - LineNumberNode(g.line, g.file), - Expr(:meta, :push_loc, g.file, Symbol("@generated body")), + source, + Expr(:meta, :push_loc, file, :var"@generated body"), Expr(:return, body), Expr(:meta, :pop_loc)))) spnames = g.spnames - if spnames === nothing + if spnames === svec() return lam else return Expr(Symbol("with-static-parameters"), lam, spnames...) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index ed74fd84ac71c..4980f051d1459 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -572,7 +572,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp break end topmost === nothing || continue - if edge_matches_sv(infstate, method, sig, sparams, hardlimit, sv) + if edge_matches_sv(interp, infstate, method, sig, sparams, hardlimit, sv) topmost = infstate edgecycle = true end @@ -680,12 +680,13 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(rt, edgecycle, edgelimited, edge, effects) end -function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) +function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) # The `method_for_inference_heuristics` will expand the given method's generator if # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists. # The other `CodeInfo`s we inspect will already have this field inflated, so we just # access it directly instead (to avoid regeneration). - callee_method2 = method_for_inference_heuristics(method, sig, sparams) # Union{Method, Nothing} + world = get_world_counter(interp) + callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing} inf_method2 = frame.src.method_for_inference_limit_heuristics # limit only if user token match inf_method2 isa Method || (inf_method2 = nothing) @@ -722,11 +723,11 @@ function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(si end # This function is used for computing alternate limit heuristics -function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector) - if isdefined(method, :generator) && method.generator.expand_early && may_invoke_generator(method, sig, sparams) +function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector, world::UInt) + if isdefined(method, :generator) && !(method.generator isa Core.GeneratedFunctionStub) && may_invoke_generator(method, sig, sparams) method_instance = specialize_method(method, sig, sparams) if isa(method_instance, MethodInstance) - cinfo = get_staged(method_instance) + cinfo = get_staged(method_instance, world) if isa(cinfo, CodeInfo) method2 = cinfo.method_for_inference_limit_heuristics if method2 isa Method diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl index 77b36cb9c7f71..1f62d21c9d2d9 100644 --- a/base/compiler/bootstrap.jl +++ b/base/compiler/bootstrap.jl @@ -36,7 +36,7 @@ let interp = NativeInterpreter() else tt = Tuple{typeof(f), Vararg{Any}} end - for m in _methods_by_ftype(tt, 10, typemax(UInt))::Vector + for m in _methods_by_ftype(tt, 10, get_world_counter())::Vector # remove any TypeVars from the intersection m = m::MethodMatch typ = Any[m.spec_types.parameters...] diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index aea657404b249..48d25243417fe 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -366,7 +366,8 @@ end function InferenceState(result::InferenceResult, cache::Symbol, interp::AbstractInterpreter) # prepare an InferenceState object for inferring lambda - src = retrieve_code_info(result.linfo) + world = get_world_counter(interp) + src = retrieve_code_info(result.linfo, world) src === nothing && return nothing validate_code_in_debug_mode(result.linfo, src, "lowered") return InferenceState(result, src, cache, interp) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 4bdde801614be..1b5f45fac3e2e 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -181,7 +181,8 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, interp::Abstrac return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end function OptimizationState(linfo::MethodInstance, interp::AbstractInterpreter) - src = retrieve_code_info(linfo) + world = get_world_counter(interp) + src = retrieve_code_info(linfo, world) src === nothing && return nothing return OptimizationState(linfo, src, interp) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 4cea01644737c..7bebd9d7d64e5 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1030,7 +1030,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_sysimg() - return retrieve_code_info(mi) + return retrieve_code_info(mi, get_world_counter(interp)) end lock_mi_inference(interp, mi) result = InferenceResult(mi, typeinf_lattice(interp)) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index d3a13ec937f88..91d585cdd76ff 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -341,7 +341,7 @@ function NativeInterpreter(world::UInt = get_world_counter(); inf_params::InferenceParams = InferenceParams(), opt_params::OptimizationParams = OptimizationParams()) # Sometimes the caller is lazy and passes typemax(UInt). - # we cap it to the current world age + # we cap it to the current world age for correctness if world == typemax(UInt) world = get_world_counter() end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 3c8357cf3a414..bae8ef5bae242 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -114,23 +114,23 @@ end invoke_api(li::CodeInstance) = ccall(:jl_invoke_api, Cint, (Any,), li) use_const_api(li::CodeInstance) = invoke_api(li) == 2 -function get_staged(mi::MethodInstance) +function get_staged(mi::MethodInstance, world::UInt) may_invoke_generator(mi) || return nothing try # user code might throw errors – ignore them - ci = ccall(:jl_code_for_staged, Any, (Any,), mi)::CodeInfo + ci = ccall(:jl_code_for_staged, Any, (Any, UInt), mi, world)::CodeInfo return ci catch return nothing end end -function retrieve_code_info(linfo::MethodInstance) +function retrieve_code_info(linfo::MethodInstance, world::UInt) m = linfo.def::Method c = nothing if isdefined(m, :generator) # user code might throw errors – ignore them - c = get_staged(linfo) + c = get_staged(linfo, world) end if c === nothing && isdefined(m, :source) src = m.source diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 22a21a4559985..68eb2ab15c59d 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -200,15 +200,14 @@ end """ validate_code!(errors::Vector{InvalidCodeError}, mi::MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) + c::Union{Nothing,CodeInfo}) Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is -the `CodeInfo` instance associated with `mi`. +a `CodeInfo` instance associated with `mi`. """ -function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) +function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, c::Union{Nothing,CodeInfo}) is_top_level = mi.def isa Module if is_top_level mnargs = 0 diff --git a/base/expr.jl b/base/expr.jl index 46e89bf64da8a..5649303b41ef4 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -962,10 +962,7 @@ macro generated(f) Expr(:block, lno, Expr(:if, Expr(:generated), - # https://github.com/JuliaLang/julia/issues/25678 - Expr(:block, - :(local $tmp = $body), - :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)), + body, Expr(:block, Expr(:meta, :generated_only), Expr(:return, nothing)))))) diff --git a/base/reflection.jl b/base/reflection.jl index 0e3d5e1fb82a7..5ac74e1c75bab 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -961,10 +961,11 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end - return map(method_instances(f, t)) do m + world = get_world_counter() + return map(method_instances(f, t, world)) do m if generated && hasgenerator(m) if may_invoke_generator(m) - return ccall(:jl_code_for_staged, Any, (Any,), m)::CodeInfo + return ccall(:jl_code_for_staged, Any, (Any, UInt), m, world)::CodeInfo else error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", @@ -1053,6 +1054,8 @@ methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") min = RefValue{UInt}(typemin(UInt)) max = RefValue{UInt}(typemax(UInt)) ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector @@ -1127,9 +1130,11 @@ _uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompres const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir -function method_instances(@nospecialize(f), @nospecialize(t), world::UInt=get_world_counter()) +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt) tt = signature_type(f, t) results = Core.MethodInstance[] + # this make a better error message than the typeassert that follows + world == typemax(UInt) && error("code reflection cannot be used from generated functions") for match in _methods_by_ftype(tt, -1, world)::Vector instance = Core.Compiler.specialize_method(match) push!(results, instance) @@ -1200,20 +1205,22 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim # generator only has one method generator = method.generator isa(generator, Core.GeneratedFunctionStub) || return false - gen_mthds = methods(generator.gen)::MethodList - length(gen_mthds) == 1 || return false + gen_mthds = _methods_by_ftype(Tuple{typeof(generator.gen), Vararg{Any}}, 1, method.primary_world) + (gen_mthds isa Vector && length(gen_mthds) == 1) || return false - generator_method = first(gen_mthds) + generator_method = first(gen_mthds).method nsparams = length(sparams) isdefined(generator_method, :source) || return false code = generator_method.source nslots = ccall(:jl_ir_nslots, Int, (Any,), code) - at = unwrap_unionall(atype)::DataType + at = unwrap_unionall(atype) + at isa DataType || return false (nslots >= 1 + length(sparams) + length(at.parameters)) || return false + firstarg = 1 for i = 1:nsparams if isa(sparams[i], TypeVar) - if (ast_slotflag(code, 1 + i) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 return false end end @@ -1222,7 +1229,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim non_va_args = method.isva ? nargs - 1 : nargs for i = 1:non_va_args if !isdispatchelem(at.parameters[i]) - if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 return false end end @@ -1230,7 +1237,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim if method.isva # If the va argument is used, we need to ensure that all arguments that # contribute to the va tuple are dispatchelemes - if (ast_slotflag(code, 1 + nargs + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 for i = (non_va_args+1):length(at.parameters) if !isdispatchelem(at.parameters[i]) return false @@ -1320,7 +1327,8 @@ function code_typed_by_type(@nospecialize(tt::Type); debuginfo::Symbol=:default, world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default @@ -1429,7 +1437,7 @@ function code_ircode_by_type( interp = Core.Compiler.NativeInterpreter(world), optimize_until::Union{Integer,AbstractString,Nothing} = nothing, ) - ccall(:jl_is_in_pure_context, Bool, ()) && + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, -1, world)::Vector @@ -1456,7 +1464,8 @@ end function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if isa(f, Core.OpaqueClosure) _, rt = only(code_typed_opaque_closure(f)) return Any[rt] @@ -1480,7 +1489,8 @@ end function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) types = to_tuple_type(types) argtypes = Any[Core.Compiler.Const(f), types.parameters...] @@ -1556,6 +1566,7 @@ function _which(@nospecialize(tt::Type); method_table::Union{Nothing,Core.MethodTable,Core.Compiler.MethodTableView}=nothing, world::UInt=get_world_counter(), raise::Bool=true) + world == typemax(UInt) && error("code reflection cannot be used from generated functions") if method_table === nothing table = Core.Compiler.InternalMethodTable(world) elseif method_table isa Core.MethodTable diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index df6a2224c2a48..76b0cc97df5bf 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -685,10 +685,10 @@ A (usually temporary) container for holding lowered source code. A `UInt8` array of slot properties, represented as bit flags: - * 2 - assigned (only false if there are *no* assignment statements with this var on the left) - * 8 - const (currently unused for local variables) - * 16 - statically assigned once - * 32 - might be used before assigned. This flag is only valid after type inference. + * 0x02 - assigned (only false if there are *no* assignment statements with this var on the left) + * 0x08 - used (if there is any read or write of the slot) + * 0x10 - statically assigned once + * 0x20 - might be used before assigned. This flag is only valid after type inference. * `ssavaluetypes` diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 43f933aed28c7..76d8967133629 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -2023,7 +2023,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz if (src) jlrettype = src->rettype; else if (jl_is_method(mi->def.method)) { - src = mi->def.method->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)mi->def.method->source; + src = mi->def.method->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)mi->def.method->source; if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method)) src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); } diff --git a/src/ast.c b/src/ast.c index cb03b7efb6eb7..3f3d6176d342e 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1024,10 +1024,10 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule jl_value_t *result; JL_TRY { margs[0] = jl_toplevel_eval(*ctx, margs[0]); - jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, world); + jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, ct->world_age); JL_GC_PROMISE_ROOTED(mfunc); if (mfunc == NULL) { - jl_method_error(margs[0], &margs[1], nargs, world); + jl_method_error(margs[0], &margs[1], nargs, ct->world_age); // unreachable } *ctx = mfunc->def.method->module; diff --git a/src/gf.c b/src/gf.c index fe858d15af1cc..0b01f5e8e6ee2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -27,6 +27,9 @@ extern "C" { JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { + jl_task_t *ct = jl_current_task; + if (ct->ptls->in_pure_callback) + return ~(size_t)0; return jl_atomic_load_acquire(&jl_world_counter); } @@ -2392,7 +2395,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t // if that didn't work and compilation is off, try running in the interpreter if (compile_option == JL_OPTIONS_COMPILE_OFF || compile_option == JL_OPTIONS_COMPILE_MIN) { - jl_code_info_t *src = jl_code_for_interpreter(mi); + jl_code_info_t *src = jl_code_for_interpreter(mi, world); if (!jl_code_requires_compiler(src, 0)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, @@ -3235,6 +3238,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int intersections, size_t world, int cache_result, size_t *min_valid, size_t *max_valid, int *ambig) { + if (world > jl_atomic_load_acquire(&jl_world_counter)) + return jl_nothing; // the future is not enumerable int has_ambiguity = 0; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)type); assert(jl_is_datatype(unw)); diff --git a/src/interpreter.c b/src/interpreter.c index 08cb87791c5a3..713887f234898 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -626,7 +626,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, // preparing method IR for interpreter -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) { jl_code_info_t *src = (jl_code_info_t*)jl_atomic_load_relaxed(&mi->uninferred); if (jl_is_method(mi->def.value)) { @@ -636,7 +636,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) } else { assert(mi->def.method->generator); - src = jl_code_for_staged(mi); + src = jl_code_for_staged(mi, world); } } if (src && (jl_value_t*)src != jl_nothing) { @@ -659,7 +659,9 @@ jl_value_t *NOINLINE jl_fptr_interpret_call(jl_value_t *f, jl_value_t **args, ui { interpreter_state *s; jl_method_instance_t *mi = codeinst->def; - jl_code_info_t *src = jl_code_for_interpreter(mi); + jl_task_t *ct = jl_current_task; + size_t world = ct->world_age; + jl_code_info_t *src = jl_code_for_interpreter(mi, world); jl_array_t *stmts = src->code; assert(jl_typeis(stmts, jl_array_any_type)); unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src) + 2; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 5d19e67024f99..79e2ef1436704 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -514,7 +514,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) // TODO: this is wrong assert(def->generator); // TODO: jl_code_for_staged can throw - src = jl_code_for_staged(unspec->def); + src = jl_code_for_staged(unspec->def, ~(size_t)0); } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(def, NULL, (jl_array_t*)src); @@ -574,7 +574,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, if (jl_is_method(def)) { if (!src) { // TODO: jl_code_for_staged can throw - src = def->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)def->source; + src = def->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)def->source; } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4a0407e019432..2a4e95ab1da86 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -382,13 +382,8 @@ `((meta generated (new (core GeneratedFunctionStub) ,gname - ,(cons 'list anames) - ,(if (null? sparams) - 'nothing - (cons 'list (map car sparams))) - ,(cadr loc) - (inert ,(caddr loc)) - (false)))))) + (call (core svec) ,@(map quotify anames)) + (call (core svec) ,@(map quotify names))))))) (list gf)) '())) (types (llist-types argl)) diff --git a/src/julia.h b/src/julia.h index 4bb34cf653381..2186ee346418d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1498,7 +1498,7 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, _Atomic(jl_value_t*) *bp, jl_binding_t *bnd); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_box_bool(int8_t x) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index 0e5e3b6cdd6ef..223a245b5a7b1 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -621,7 +621,7 @@ jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tupletype_t *types, size_t world); JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT); +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT, size_t world); int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile); jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ast); JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); diff --git a/src/method.c b/src/method.c index 02855e593ef7a..ee333bebccedf 100644 --- a/src/method.c +++ b/src/method.c @@ -518,21 +518,21 @@ void jl_add_function_name_to_lineinfo(jl_code_info_t *ci, jl_value_t *name) } // invoke (compiling if necessary) the jlcall function pointer for a method template -STATIC_INLINE jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, jl_svec_t *sparam_vals, - jl_value_t **args, uint32_t nargs) +static jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, + size_t world, jl_svec_t *sparam_vals, jl_value_t **args, uint32_t nargs) { size_t n_sparams = jl_svec_len(sparam_vals); jl_value_t **gargs; - size_t totargs = 1 + n_sparams + nargs + def->isva; + size_t totargs = 2 + n_sparams + def->nargs; JL_GC_PUSHARGS(gargs, totargs); - gargs[0] = generator; - memcpy(&gargs[1], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); - memcpy(&gargs[1 + n_sparams], args, nargs * sizeof(void*)); - if (def->isva) { - gargs[totargs-1] = jl_f_tuple(NULL, &gargs[1 + n_sparams + def->nargs - 1], nargs - (def->nargs - 1)); - gargs[1 + n_sparams + def->nargs - 1] = gargs[totargs - 1]; - } - jl_value_t *code = jl_apply(gargs, 1 + n_sparams + def->nargs); + gargs[0] = jl_box_ulong(world); + gargs[1] = jl_box_long(def->line); + gargs[1] = jl_new_struct(jl_linenumbernode_type, gargs[1], def->file); + memcpy(&gargs[2], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); + memcpy(&gargs[2 + n_sparams], args, (def->nargs - def->isva) * sizeof(void*)); + if (def->isva) + gargs[totargs - 1] = jl_f_tuple(NULL, &args[def->nargs - 1], nargs - def->nargs + 1); + jl_value_t *code = jl_apply_generic(generator, gargs, totargs); JL_GC_POP(); return code; } @@ -556,7 +556,7 @@ JL_DLLEXPORT jl_code_info_t *jl_expand_and_resolve(jl_value_t *ex, jl_module_t * // Return a newly allocated CodeInfo for the function signature // effectively described by the tuple (specTypes, env, Method) inside linfo -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world) { jl_value_t *uninferred = jl_atomic_load_relaxed(&linfo->uninferred); if (uninferred) { @@ -580,13 +580,13 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) JL_TRY { ct->ptls->in_pure_callback = 1; - // and the right world ct->world_age = def->primary_world; // invoke code generator jl_tupletype_t *ttdt = (jl_tupletype_t*)jl_unwrap_unionall(tt); - ex = jl_call_staged(def, generator, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + ex = jl_call_staged(def, generator, world, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + // do some post-processing if (jl_is_code_info(ex)) { func = (jl_code_info_t*)ex; jl_array_t *stmts = (jl_array_t*)func->code; @@ -603,7 +603,6 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) jl_error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator."); } } - jl_add_function_name_to_lineinfo(func, (jl_value_t*)def->name); // If this generated function has an opaque closure, cache it for @@ -743,20 +742,12 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) st = jl_nothing; } else if (nargs == 2 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_sym) { - m->generator = NULL; + if (m->generator != NULL) + jl_error("duplicate @generated function body"); jl_value_t *gexpr = jl_exprarg(st, 1); - if (jl_expr_nargs(gexpr) == 7) { - // expects (new (core GeneratedFunctionStub) funcname argnames sp line file expandearly) - jl_value_t *funcname = jl_exprarg(gexpr, 1); - assert(jl_is_symbol(funcname)); - if (jl_get_global(m->module, (jl_sym_t*)funcname) != NULL) { - m->generator = jl_toplevel_eval(m->module, gexpr); - jl_gc_wb(m, m->generator); - } - } - if (m->generator == NULL) { - jl_error("invalid @generated function; try placing it in global scope"); - } + // the frontend would put (new (core GeneratedFunctionStub) funcname argnames sp) here, for example + m->generator = jl_toplevel_eval(m->module, gexpr); + jl_gc_wb(m, m->generator); st = jl_nothing; } else if (nargs == 1 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_only_sym) { diff --git a/test/ambiguous.jl b/test/ambiguous.jl index e96954299b702..4ab779d76e6f0 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -357,7 +357,7 @@ f35983(::Type, ::Type) = 2 @test length(Base.methods(f35983, (Any, Any))) == 2 @test first(Base.methods(f35983, (Any, Any))).sig == Tuple{typeof(f35983), Type, Type} let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 0 @@ -366,7 +366,7 @@ f35983(::Type{Int16}, ::Any) = 3 @test length(Base.methods_including_ambiguous(f35983, (Type, Type))) == 2 @test length(Base.methods(f35983, (Type, Type))) == 1 let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 2 @test ambig[] == 1 @@ -374,7 +374,7 @@ end struct B38280 <: Real; val; end let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, typemax(UInt), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 1 diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 740e985e388df..9db7ae1aeaa5d 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -7,7 +7,7 @@ module MiniCassette # A minimal demonstration of the cassette mechanism. Doesn't support all the # fancy features, but sufficient to exercise this code path in the compiler. - using Core.Compiler: method_instances, retrieve_code_info, CodeInfo, + using Core.Compiler: retrieve_code_info, CodeInfo, MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, signature_type using Base: _methods_by_ftype @@ -69,24 +69,28 @@ module MiniCassette end end - function overdub_generator(self, c, f, args) + function overdub_generator(world::UInt, source, self, c, f, args) + @nospecialize if !Base.issingletontype(f) - return :(return f(args...)) + # (c, f, args..) -> f(args...) + code_info = :(return f(args...)) + return Core.GeneratedFunctionStub(identity, Core.svec(:overdub, :c, :f, :args), Core.svec())(world, source, code_info) end tt = Tuple{f, args...} - match = Base._which(tt; world=typemax(UInt)) + match = Base._which(tt; world) mi = Core.Compiler.specialize_method(match) # Unsupported in this mini-cassette @assert !mi.def.isva - code_info = retrieve_code_info(mi) + code_info = retrieve_code_info(mi, world) @assert isa(code_info, CodeInfo) code_info = copy(code_info) - if isdefined(code_info, :edges) - code_info.edges = MethodInstance[mi] - end + @assert code_info.edges === nothing + code_info.edges = MethodInstance[mi] transform!(code_info, length(args), match.sparams) - code_info + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) + return code_info end @inline function overdub(c::Ctx, f::Union{Core.Builtin, Core.IntrinsicFunction}, args...) @@ -95,16 +99,7 @@ module MiniCassette @eval function overdub(c::Ctx, f, args...) $(Expr(:meta, :generated_only)) - $(Expr(:meta, - :generated, - Expr(:new, - Core.GeneratedFunctionStub, - :overdub_generator, - Any[:overdub, :ctx, :f, :args], - Any[], - @__LINE__, - QuoteNode(Symbol(@__FILE__)), - true))) + $(Expr(:meta, :generated, overdub_generator)) end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e466dc8a3739a..12fedf2792a61 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1788,9 +1788,17 @@ bar_22708(x) = f_22708(x) @test bar_22708(1) == "x" +struct EarlyGeneratedFunctionStub + stub::Core.GeneratedFunctionStub +end +(stub::EarlyGeneratedFunctionStub)(args...) = (@nospecialize; stub.stub(args...)) + # mechanism for spoofing work-limiting heuristics and early generator expansion (#24852) -function _generated_stub(gen::Symbol, args::Vector{Any}, params::Vector{Any}, line, file, expand_early) - stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params, line, file, expand_early) +function _generated_stub(gen::Symbol, args::Core.SimpleVector, params::Core.SimpleVector, expand_early::Bool) + stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params) + if expand_early + stub = Expr(:new, EarlyGeneratedFunctionStub, stub) + end return Expr(:meta, :generated, stub) end @@ -1799,10 +1807,21 @@ f24852_kernel2(x, y::Tuple) = f24852_kernel1(x, (y,)) f24852_kernel3(x, y::Tuple) = f24852_kernel2(x, (y,)) f24852_kernel(x, y::Number) = f24852_kernel3(x, (y,)) -function f24852_kernel_cinfo(fsig::Type) - world = typemax(UInt) # FIXME - match = Base._methods_by_ftype(fsig, -1, world)[1] - isdefined(match.method, :source) || return (nothing, :(f(x, y))) +function f24852_kernel_cinfo(world::UInt, source, fsig::Type) + matches = Base._methods_by_ftype(fsig, -1, world) + if matches === nothing || length(matches) != 1 + match = nothing + else + match = matches[1] + if !isdefined(match.method, :source) + match = nothing + end + end + if match === nothing + code_info = :(f(x, y)) + code_info = Core.GeneratedFunctionStub(identity, Core.svec(:self, :f, :x, :y), Core.svec(:X, :Y))(world, source, code_info) + return (nothing, code_info) + end code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 1, 0, :propagate) if startswith(String(match.method.name), "f24852") @@ -1817,21 +1836,23 @@ function f24852_kernel_cinfo(fsig::Type) end pushfirst!(code_info.slotnames, Symbol("#self#")) pushfirst!(code_info.slotflags, 0x00) + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return match.method, code_info end -function f24852_gen_cinfo_uninflated(X, Y, _, f, x, y) - _, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) +function f24852_gen_cinfo_uninflated(world::UInt, source, X, Y, _, f, x, y) + _, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) return code_info end -function f24852_gen_cinfo_inflated(X, Y, _, f, x, y) - method, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) +function f24852_gen_cinfo_inflated(world::UInt, source, X, Y, _, f, x, y) + method, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) code_info.method_for_inference_limit_heuristics = method return code_info end -function f24852_gen_expr(X, Y, _, f, x, y) # deparse f(x::X, y::Y) where {X, Y} +function f24852_gen_expr(X, Y, _, f, x, y) # deparse of f(x::X, y::Y) where {X, Y} if f === typeof(f24852_kernel) f2 = :f24852_kernel3 elseif f === typeof(f24852_kernel3) @@ -1848,20 +1869,8 @@ end @eval begin function f24852_late_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) - $(Expr(:meta, :generated_only)) - #= no body =# - end - function f24852_late_inflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) - $(Expr(:meta, :generated_only)) - #= no body =# - end - function f24852_late_uninflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) + $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), + Core.svec(:X, :Y), false)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1869,20 +1878,18 @@ end @eval begin function f24852_early_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), + Core.svec(:X, :Y), true)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_inflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(Expr(:meta, :generated, f24852_gen_cinfo_inflated)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_uninflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(Expr(:meta, :generated, f24852_gen_cinfo_uninflated)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1893,10 +1900,6 @@ result = f24852_kernel(x, y) @test result === f24852_late_expr(f24852_kernel, x, y) @test Base.return_types(f24852_late_expr, typeof((f24852_kernel, x, y))) == Any[Any] -@test result === f24852_late_uninflated(f24852_kernel, x, y) -@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] -@test result === f24852_late_uninflated(f24852_kernel, x, y) -@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === f24852_early_expr(f24852_kernel, x, y) @test Base.return_types(f24852_early_expr, typeof((f24852_kernel, x, y))) == Any[Any] @@ -1904,7 +1907,6 @@ result = f24852_kernel(x, y) @test Base.return_types(f24852_early_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === @inferred f24852_early_inflated(f24852_kernel, x, y) @test Base.return_types(f24852_early_inflated, typeof((f24852_kernel, x, y))) == Any[Float64] - # TODO: test that `expand_early = true` + inflated `method_for_inference_limit_heuristics` # can be used to tighten up some inference result. diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index c25aae71ab157..5fd074fee73ae 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -22,10 +22,9 @@ msig = Tuple{typeof(f22938),Int,Int,Int,Int} world = Base.get_world_counter() match = only(Base._methods_by_ftype(msig, -1, world)) mi = Core.Compiler.specialize_method(match) -c0 = Core.Compiler.retrieve_code_info(mi) +c0 = Core.Compiler.retrieve_code_info(mi, world) -@test isempty(Core.Compiler.validate_code(mi)) -@test isempty(Core.Compiler.validate_code(c0)) +@test isempty(Core.Compiler.validate_code(mi, c0)) @testset "INVALID_EXPR_HEAD" begin c = copy(c0) @@ -116,7 +115,7 @@ end @testset "SIGNATURE_NARGS_MISMATCH" begin old_sig = mi.def.sig mi.def.sig = Tuple{1,2} - errors = Core.Compiler.validate_code(mi) + errors = Core.Compiler.validate_code(mi, nothing) mi.def.sig = old_sig @test length(errors) == 1 @test errors[1].kind === Core.Compiler.SIGNATURE_NARGS_MISMATCH @@ -132,7 +131,7 @@ end @testset "SLOTNAMES_NARGS_MISMATCH" begin mi.def.nargs += 20 - errors = Core.Compiler.validate_code(mi) + errors = Core.Compiler.validate_code(mi, c0) mi.def.nargs -= 20 @test length(errors) == 2 @test count(e.kind === Core.Compiler.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 diff --git a/test/core.jl b/test/core.jl index 83ede23b9850e..8f8b8c1a28bcd 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3883,7 +3883,8 @@ PossiblyInvalidUnion{T} = Union{T,Int} # issue #13007 call13007(::Type{Array{T,N}}) where {T,N} = 0 call13007(::Type{Array}) = 1 -@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt))) == 2 +@test Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt)) === nothing +@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, Base.get_world_counter())) == 2 # detecting cycles during type intersection, e.g. #1631 cycle_in_solve_tvar_constraints(::Type{Some{S}}, x::S) where {S} = 0 diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 7db55ff7f0615..6bd86f7f3a4d5 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -180,28 +180,23 @@ mk_va_opaque() = @opaque (x...)->x @test repr(@opaque x->1) == "(::Any)::Any->◌" # Opaque closure in CodeInfo returned from generated functions -function mk_ocg(args...) - ci = @code_lowered const_int() - cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, - Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] - cig.slotnames = Symbol[Symbol("#self#")] - cig.slottypes = Any[Any] - cig.slotflags = UInt8[0x00] - cig +let ci = @code_lowered const_int() + global function mk_ocg(world::UInt, source, args...) + @nospecialize + cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, + Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] + cig.slotnames = Symbol[Symbol("#self#")] + cig.slottypes = Any[Any] + cig.slotflags = UInt8[0x00] + @assert cig.min_world == UInt(1) + @assert cig.max_world == typemax(UInt) + return cig + end end @eval function oc_trivial_generated() $(Expr(:meta, :generated_only)) - $(Expr(:meta, - :generated, - Expr(:new, - Core.GeneratedFunctionStub, - :mk_ocg, - Any[:oc_trivial_generated], - Any[], - @__LINE__, - QuoteNode(Symbol(@__FILE__)), - true))) + $(Expr(:meta, :generated, mk_ocg)) end @test isa(oc_trivial_generated(), OpaqueClosure{Tuple{}, Any}) @test oc_trivial_generated()() == 1 diff --git a/test/reflection.jl b/test/reflection.jl index e926cdf0a2b9f..95965bf1725a7 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -648,7 +648,7 @@ let world = Core.Compiler.get_world_counter() match = Base._methods_by_ftype(T22979, -1, world)[1] instance = Core.Compiler.specialize_method(match) - cinfo_generated = Core.Compiler.get_staged(instance) + cinfo_generated = Core.Compiler.get_staged(instance, world) @test_throws ErrorException Base.uncompressed_ir(match.method) test_similar_codeinfo(code_lowered(f22979, typeof(x22979))[1], cinfo_generated) diff --git a/test/staged.jl b/test/staged.jl index 4a7fa3d7f4c84..0fa8ecb182cff 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -196,12 +196,11 @@ let gf_err2 return nothing end Expected = ErrorException("code reflection cannot be used from generated functions") + @test_throws Expected gf_err2(code_lowered) @test_throws Expected gf_err2(code_typed) @test_throws Expected gf_err2(code_llvm) @test_throws Expected gf_err2(code_native) - @test gf_err_ref[] == 66 - @test gf_err2(code_lowered) === nothing - @test gf_err_ref[] == 1077 + @test gf_err_ref[] == 88 end # issue #15043 @@ -246,12 +245,18 @@ f22440kernel(x::AbstractFloat) = x * x f22440kernel(::Type{T}) where {T} = one(T) f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) -@generated function f22440(y) - match = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] +function f22440_gen(world::UInt, source, _, y) + match = only(Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, world)) code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 0, 0, :propagate) + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return code_info end +@eval function f22440(y) + $(Expr(:meta, :generated, f22440_gen)) + $(Expr(:meta, :generated_only)) +end @test f22440(Int) === f22440kernel(Int) @test f22440(Float64) === f22440kernel(Float64) @@ -309,26 +314,33 @@ end # https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12 # generated function with varargs and unfortunately placed unused slot @generated function f_vararg_generated(args...) + local unusedslot4 + local unusedslot5 + local unusedslot6 :($args) end g_vararg_generated() = f_vararg_generated((;), (;), Base.inferencebarrier((;))) let tup = g_vararg_generated() @test all(==(typeof((;))), tup) - # This is just to make sure that the test is actually testing what we want - - # the test only works if there's an unused that matches the position of the - # inferencebarrier argument above (N.B. the generator function itself + # This is just to make sure that the test is actually testing what we want: + # the test only works if there is an unused that matches the position of + # the inferencebarrier argument above (N.B. the generator function itself # shifts everything over by 1) - @test only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == UInt8(0x00) + @test_broken only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == 0x00 end # respect a given linetable in code generation # https://github.com/JuliaLang/julia/pull/47750 -let match = Base._which(Tuple{typeof(sin),Int}) +let world = Base.get_world_counter() + match = Base._which(Tuple{typeof(sin), Int}; world) mi = Core.Compiler.specialize_method(match) - lwr = Core.Compiler.retrieve_code_info(mi) - @test all(lin->lin.method===:sin, lwr.linetable) - @generated sin_generated(a) = lwr + lwr = Core.Compiler.retrieve_code_info(mi, world) + @test all(lin->lin.method === :sin, lwr.linetable) + @eval function sin_generated(a) + $(Expr(:meta, :generated, Returns(lwr))) + $(Expr(:meta, :generated_only)) + end src = only(code_lowered(sin_generated, (Int,))) - @test all(lin->lin.method===:sin, src.linetable) + @test all(lin->lin.method === :sin, src.linetable) @test sin_generated(42) == sin(42) end diff --git a/test/syntax.jl b/test/syntax.jl index 756af45e6b3c7..e60bbd81a04ab 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3053,9 +3053,6 @@ end end # issue 25678 -@generated f25678(x::T) where {T} = code_lowered(sin, Tuple{x})[] -@test f25678(pi/6) === sin(pi/6) - @generated g25678(x) = return :x @test g25678(7) === 7