diff --git a/base/Base.jl b/base/Base.jl index 85a9c8d5048e3..58d527f365aa7 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -478,7 +478,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 0d0280e40e817..5fac62eb7578d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -569,7 +569,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 @@ -677,12 +677,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) @@ -719,11 +720,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 7ff740a23a540..959d2d157f219 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -363,7 +363,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 dc321be5108cf..edf678e919b61 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -183,7 +183,8 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::Optimiz return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end function OptimizationState(linfo::MethodInstance, params::OptimizationParams, 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, params, interp) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index f7723a968da3e..734c357201d25 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1035,7 +1035,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 cac15e9a69513..7bdbdbd8bdf01 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -330,7 +330,7 @@ struct NativeInterpreter <: AbstractInterpreter cache = Vector{InferenceResult}() # Initially empty cache # 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 6cf600560902d..0acbc926ae671 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 9e2615a16a190..bb7dfc0f0cf00 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 @@ -1125,9 +1128,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) @@ -1198,20 +1203,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 @@ -1220,7 +1227,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 @@ -1228,7 +1235,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 @@ -1318,7 +1325,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 @@ -1427,7 +1435,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 @@ -1454,7 +1462,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] @@ -1478,7 +1487,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...] 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 d1694eaf9e0d5..866d9dabe5100 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1088,7 +1088,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 42990baf7ad24..d8c5e571e820f 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); } @@ -2296,7 +2299,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, @@ -3139,6 +3142,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 b489665f5629d..940613fd596f4 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -499,7 +499,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, unspec->min_world); } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(def, NULL, (jl_array_t*)src); @@ -554,7 +554,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 19dab5cd3a704..083e4ef2eab02 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1495,7 +1495,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 b77de64732116..492c0ec8a8984 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 d5f56a5921358..64bec989251a8 100644 --- a/src/method.c +++ b/src/method.c @@ -517,21 +517,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; } @@ -555,7 +555,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) { @@ -579,13 +579,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; @@ -602,7 +602,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 @@ -742,20 +741,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 182776d79d7ec..5209ef879324e 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1787,9 +1787,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 @@ -1798,10 +1806,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") @@ -1816,21 +1835,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) @@ -1847,20 +1868,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 @@ -1868,20 +1877,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 @@ -1892,10 +1899,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] @@ -1903,7 +1906,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 8af7421ba7501..8507f2bdb8a01 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 b5d5f9ed522ac..d54d38403913a 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -173,28 +173,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(), Core.OpaqueClosure{Tuple{}, Any}) @test oc_trivial_generated()() == 1 diff --git a/test/reflection.jl b/test/reflection.jl index 0c1081ba2c42f..f31b77ad26aed 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