Skip to content

Commit b0615e3

Browse files
committed
Add a rendezvous mechanism for reflection functions
This adds a reflection mechanism for the various reflection functions defined by Base. These functions are mostly interactive affordances and for ease of Compiler development, it tends to be important that these reflect the user's expectation of what the compiler is. This is currently the case using the existing `Revise.track(Core.Compiler)` workflow, because it runs in the latest world-age, not the inference world. This commit adds a special binding that if present in `Main` (because it was imported by a `using Compiler`) will switch the compiler used for reflection to whatever the binding points to. In this way, we can replace the old `using Revise; Revise.track(Core.Compiler)` workflow by `using Revise, Compiler` without additional headache.
1 parent 34c4c6e commit b0615e3

File tree

4 files changed

+61
-22
lines changed

4 files changed

+61
-22
lines changed

base/loading.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,9 +2818,6 @@ function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=noth
28182818
end
28192819
end
28202820

2821-
2822-
2823-
28242821
# relative-path load
28252822

28262823
"""

base/opaque_closure.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ function compute_ir_rettype(ir::IRCode)
4545
rt = Union{}
4646
for i = 1:length(ir.stmts)
4747
stmt = ir[SSAValue(i)][:stmt]
48-
if isa(stmt, Core.Compiler.ReturnNode) && isdefined(stmt, :val)
49-
rt = Core.Compiler.tmerge(Core.Compiler.argextype(stmt.val, ir), rt)
48+
if isa(stmt, Core.ReturnNode) && isdefined(stmt, :val)
49+
rt = Compiler.tmerge(Compiler.argextype(stmt.val, ir), rt)
5050
end
5151
end
52-
return Core.Compiler.widenconst(rt)
52+
return Compiler.widenconst(rt)
5353
end
5454

5555
function compute_oc_signature(ir::IRCode, nargs::Int, isva::Bool)
5656
argtypes = Vector{Any}(undef, nargs)
5757
for i = 1:nargs
58-
argtypes[i] = Core.Compiler.widenconst(ir.argtypes[i+1])
58+
argtypes[i] = Compiler.widenconst(ir.argtypes[i+1])
5959
end
6060
if isva
6161
lastarg = pop!(argtypes)

base/reflection.jl

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -346,17 +346,41 @@ function raise_match_failure(name::Symbol, @nospecialize(tt))
346346
error("$name: unanalyzable call given $sig_str")
347347
end
348348

349+
"""
350+
default_compiler_module()
351+
352+
In order to facilitate working on the compiler, we'd like to make it easy for
353+
our users to switch all the ordinary compiler-based reflection to a new copy of the
354+
compiler, if they `dev` and load a new version of the compiler. However, it would
355+
be confusing if the result of reflection functions changed, simply because some
356+
downstream package happens to load a new copy of `Compiler`. Thus, we look for
357+
a special rendezvous binding `var"#_interactive_compiler_reference` in Main.
358+
This binding is exported by the compiler, so if the user does `using Compiler` to
359+
load a dev'ed version of the Compiler from `Main`, we will see that and pick up
360+
the new Compiler, but if the using statement is in some downstream package, we
361+
will not.
362+
"""
363+
function default_compiler_module()
364+
# If the user has explicitly loaded a new compiler into Main, prefer that.
365+
if isdefined(Core.Main, :var"#_interactive_compiler_reference")
366+
return Core.Main.var"#_interactive_compiler_reference"
367+
end
368+
# Otherwise return a private reference to the internal compiler
369+
return Base.Compiler
370+
end
371+
349372
"""
350373
code_typed_by_type(types::Type{<:Tuple}; ...)
351374
352375
Similar to [`code_typed`](@ref), except the argument is a tuple type describing
353376
a full signature to query.
354377
"""
355378
function code_typed_by_type(@nospecialize(tt::Type);
379+
Compiler::Module = default_compiler_module(),
356380
optimize::Bool=true,
357381
debuginfo::Symbol=:default,
358382
world::UInt=get_world_counter(),
359-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
383+
interp=Compiler.NativeInterpreter(world))
360384
(ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
361385
error("code reflection cannot be used from generated functions")
362386
if @isdefined(IRShow)
@@ -384,7 +408,7 @@ function code_typed_by_type(@nospecialize(tt::Type);
384408
return asts
385409
end
386410

387-
function get_oc_code_rt(oc::Core.OpaqueClosure, types, optimize::Bool)
411+
function get_oc_code_rt(Compiler::Module, oc::Core.OpaqueClosure, types, optimize::Bool)
388412
@nospecialize oc types
389413
ccall(:jl_is_in_pure_context, Bool, ()) &&
390414
error("code reflection cannot be used from generated functions")
@@ -416,11 +440,12 @@ function get_oc_code_rt(oc::Core.OpaqueClosure, types, optimize::Bool)
416440
end
417441

418442
function code_typed_opaque_closure(oc::Core.OpaqueClosure, types;
443+
Compiler::Module = default_compiler_module(),
419444
debuginfo::Symbol=:default,
420445
optimize::Bool=true,
421446
_...)
422447
@nospecialize oc types
423-
(code, rt) = get_oc_code_rt(oc, types, optimize)
448+
(code, rt) = get_oc_code_rt(Compiler, oc, types, optimize)
424449
debuginfo === :none && remove_linenums!(code)
425450
return Any[Pair{CodeInfo,Any}(code, rt)]
426451
end
@@ -484,8 +509,9 @@ a full signature to query.
484509
"""
485510
function code_ircode_by_type(
486511
@nospecialize(tt::Type);
512+
Compiler::Module = default_compiler_module(),
487513
world::UInt=get_world_counter(),
488-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world),
514+
interp=Compiler.NativeInterpreter(world),
489515
optimize_until::Union{Integer,AbstractString,Nothing}=nothing,
490516
)
491517
(ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
@@ -506,22 +532,25 @@ function code_ircode_by_type(
506532
return asts
507533
end
508534

509-
function _builtin_return_type(interp::Compiler.AbstractInterpreter,
535+
function _builtin_return_type(interp,
510536
@nospecialize(f::Core.Builtin), @nospecialize(types))
537+
Compiler = typename(typeof(interp))
511538
argtypes = Any[to_tuple_type(types).parameters...]
512539
rt = Compiler.builtin_tfunction(interp, f, argtypes, nothing)
513540
return Compiler.widenconst(rt)
514541
end
515542

516-
function _builtin_effects(interp::Compiler.AbstractInterpreter,
543+
function _builtin_effects(interp,
517544
@nospecialize(f::Core.Builtin), @nospecialize(types))
545+
Compiler = typename(typeof(interp))
518546
argtypes = Any[to_tuple_type(types).parameters...]
519547
rt = Compiler.builtin_tfunction(interp, f, argtypes, nothing)
520548
return Compiler.builtin_effects(Compiler.typeinf_lattice(interp), f, argtypes, rt)
521549
end
522550

523-
function _builtin_exception_type(interp::Compiler.AbstractInterpreter,
551+
function _builtin_exception_type(interp,
524552
@nospecialize(f::Core.Builtin), @nospecialize(types))
553+
Compiler = typename(typeof(interp))
525554
effects = _builtin_effects(interp, f, types)
526555
return Compiler.is_nothrow(effects) ? Union{} : Any
527556
end
@@ -578,11 +607,12 @@ julia> Base.return_types(sum, (Union{Vector{Int},UnitRange{Int}},))
578607
doing so will result in an error.
579608
"""
580609
function return_types(@nospecialize(f), @nospecialize(types=default_tt(f));
610+
Compiler::Module = default_compiler_module(),
581611
world::UInt=get_world_counter(),
582-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
612+
interp=Compiler.NativeInterpreter(world))
583613
check_generated_context(world)
584614
if isa(f, Core.OpaqueClosure)
585-
_, rt = only(code_typed_opaque_closure(f, types))
615+
_, rt = only(code_typed_opaque_closure(Compiler, f, types))
586616
return Any[rt]
587617
elseif isa(f, Core.Builtin)
588618
return Any[_builtin_return_type(interp, f, types)]
@@ -646,11 +676,12 @@ On the other hand `Base.infer_return_type` returns one collective result that su
646676
doing so will result in an error.
647677
"""
648678
function infer_return_type(@nospecialize(f), @nospecialize(types=default_tt(f));
679+
Compiler::Module = default_compiler_module(),
649680
world::UInt=get_world_counter(),
650-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
681+
interp=Compiler.NativeInterpreter(world))
651682
check_generated_context(world)
652683
if isa(f, Core.OpaqueClosure)
653-
return last(only(code_typed_opaque_closure(f, types)))
684+
return last(only(code_typed_opaque_closure(Compiler, f, types)))
654685
elseif isa(f, Core.Builtin)
655686
return _builtin_return_type(interp, f, types)
656687
end
@@ -716,8 +747,9 @@ julia> Base.infer_exception_types(throw_if_number, (Any,))
716747
doing so will result in an error.
717748
"""
718749
function infer_exception_types(@nospecialize(f), @nospecialize(types=default_tt(f));
750+
Compiler::Module = default_compiler_module(),
719751
world::UInt=get_world_counter(),
720-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
752+
interp=Compiler.NativeInterpreter(world))
721753
check_generated_context(world)
722754
if isa(f, Core.OpaqueClosure)
723755
return Any[Any] # TODO
@@ -795,8 +827,9 @@ signature, the exception type is widened to `MethodError`.
795827
doing so will result in an error.
796828
"""
797829
function infer_exception_type(@nospecialize(f), @nospecialize(types=default_tt(f));
830+
Compiler::Module = default_compiler_module(),
798831
world::UInt=get_world_counter(),
799-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
832+
interp=Compiler.NativeInterpreter(world))
800833
check_generated_context(world)
801834
if isa(f, Core.OpaqueClosure)
802835
return Any # TODO
@@ -880,9 +913,10 @@ signature, the `:nothrow` bit gets tainted.
880913
- [`Base.@assume_effects`](@ref): A macro for making assumptions about the effects of a method.
881914
"""
882915
function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f));
916+
Compiler::Module = default_compiler_module(),
883917
optimize::Bool=true,
884918
world::UInt=get_world_counter(),
885-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
919+
interp=Compiler.NativeInterpreter(world))
886920
check_generated_context(world)
887921
if isa(f, Core.Builtin)
888922
return _builtin_effects(interp, f, types)
@@ -916,8 +950,9 @@ function print_statement_costs(io::IO, @nospecialize(f), @nospecialize(t); kwarg
916950
end
917951

918952
function print_statement_costs(io::IO, @nospecialize(tt::Type);
953+
Compiler::Module = default_compiler_module(),
919954
world::UInt=get_world_counter(),
920-
interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world))
955+
interp=Compiler.NativeInterpreter(world))
921956
tt = to_tuple_type(tt)
922957
world == typemax(UInt) && error("code reflection cannot be used from generated functions")
923958
matches = Compiler.findall(tt, Compiler.method_table(interp))

stdlib/Compiler/src/Compiler.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Compiler,
2727
(0x807dbc54_b67e_4c79, 0x8afb_eafe4df6f2e1))
2828
ccall(:jl_set_module_parent, Cvoid, (Any, Any), Compiler, Compiler)
2929

30+
# The interactive reflection utilities (`code_`, etc.) look for this symbol
31+
# in Main and if present use that Compiler instead of the builtin one. By
32+
# exporting this symbol, we can make `using Compiler` switch over these utilities
33+
# to the loaded compiler.
34+
const var"#_interactive_compiler_reference" = Compiler
35+
export var"#_interactive_compiler_reference"
36+
3037
using Core.Intrinsics, Core.IR
3138

3239
import Core: print, println, show, write, unsafe_write,

0 commit comments

Comments
 (0)