Skip to content

Commit 6895cbf

Browse files
committed
Move typename and <: to Core and have inference check by value
As mentioned in #55271, the `istopfunction` binding-based comparisons are problematic. In #55272 and #55273, I attempted to remove the inference special cases for `>:` and `typename` (respectively) entirely, but for differing reasons (`>:` gets too many extra specializations, `typename` loses precision), those PRs are suboptimal. As discussed in #55273, this PR instead moves these functions to Core, so that both `Core.Compiler` and `Base` share the function object, allowing inference to detect them and apply the special handling by simple value-comparison.
1 parent c49f4aa commit 6895cbf

File tree

6 files changed

+38
-13
lines changed

6 files changed

+38
-13
lines changed

base/boot.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,28 @@ const check_top_bit = check_sign_bit
10121012
EnterNode(old::EnterNode, new_dest::Int) = isdefined(old, :scope) ?
10131013
EnterNode(new_dest, old.scope) : EnterNode(new_dest)
10141014

1015+
# `typename` has special tfunc support in inference to improve
1016+
# the result for `Type{Union{...}}`. It is defined here, so that the Compiler
1017+
# can look it up by value.
1018+
struct TypeNameError
1019+
a
1020+
TypeNameError(@nospecialize(a)) = new(a)
1021+
end
1022+
1023+
typename(a) = throw(TypeNameError(a))
1024+
typename(a::DataType) = a.name
1025+
function typename(a::Union)
1026+
ta = typename(a.a)
1027+
tb = typename(a.b)
1028+
ta === tb || throw(TypeNameError(a))
1029+
return tb
1030+
end
1031+
typename(union::UnionAll) = typename(union.body)
1032+
1033+
# Special inference support to avoid execess specialization of this method.
1034+
# TODO: Replace this by a generic heuristic.
1035+
(>:)(@nospecialize(a), @nospecialize(b)) = (b <: a)
1036+
10151037
include(Core, "optimized_generics.jl")
10161038

10171039
ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Core, true)

base/compiler/abstractinterpretation.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,7 +2239,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
22392239
return CallMeta(Const(rty.val === false), Bottom, EFFECTS_TOTAL, MethodResultPure())
22402240
end
22412241
return call
2242-
elseif la == 3 && istopfunction(f, :(>:))
2242+
elseif la == 3 && f === Core.:(>:)
22432243
# mark issupertype as a exact alias for issubtype
22442244
# swap T1 and T2 arguments and call <:
22452245
if fargs !== nothing && length(fargs) == 3
@@ -2249,7 +2249,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
22492249
end
22502250
argtypes = Any[typeof(<:), argtypes[3], argtypes[2]]
22512251
return abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), si, sv, max_methods)
2252-
elseif la == 2 && istopfunction(f, :typename)
2252+
elseif la == 2 && f === Core.typename
22532253
return CallMeta(typename_static(argtypes[2]), Bottom, EFFECTS_TOTAL, MethodResultPure())
22542254
elseif f === Core._hasmethod
22552255
return _hasmethod_tfunc(interp, argtypes, sv)

base/compiler/ssair/inlining.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1738,7 +1738,7 @@ function late_inline_special_case!(ir::IRCode, idx::Int, stmt::Expr, flag::UInt3
17381738
cmp_call_ssa = insert_node!(ir, idx, removable_if_unused(NewInstruction(cmp_call, Bool)))
17391739
not_call = Expr(:call, GlobalRef(Core.Intrinsics, :not_int), cmp_call_ssa)
17401740
return SomeCase(not_call)
1741-
elseif length(argtypes) == 3 && istopfunction(f, :(>:))
1741+
elseif length(argtypes) == 3 && f === Core.:(>:)
17421742
# special-case inliner for issupertype
17431743
# that works, even though inference generally avoids inferring the `>:` Method
17441744
if isa(type, Const) && has_flag(flag, IR_FLAG_NOTHROW)

base/errorshow.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ function showerror(io::IO, ex::Meta.ParseError)
4343
end
4444
end
4545

46+
function showerror(io::IO, ex::Core.TypeNameError)
47+
print(io, "TypeNameError: ")
48+
if isa(ex.a, Union)
49+
print(io, "typename does not apply to unions whose components have different typenames")
50+
else
51+
print(io, "typename does not apply to this type")
52+
end
53+
end
54+
4655
function showerror(io::IO, ex::BoundsError)
4756
print(io, "BoundsError")
4857
if isdefined(ex, :a)

base/essentials.jl

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -575,15 +575,7 @@ function unconstrain_vararg_length(va::Core.TypeofVararg)
575575
return Vararg{unwrapva(va)}
576576
end
577577

578-
typename(a) = error("typename does not apply to this type")
579-
typename(a::DataType) = a.name
580-
function typename(a::Union)
581-
ta = typename(a.a)
582-
tb = typename(a.b)
583-
ta === tb || error("typename does not apply to unions whose components have different typenames")
584-
return tb
585-
end
586-
typename(union::UnionAll) = typename(union.body)
578+
const typename = Core.typename
587579

588580
_tuple_error(T::Type, x) = (@noinline; throw(MethodError(convert, (T, x))))
589581

base/operators.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ but which do not execute the operator or return a Bool:
5858
"""
5959
(<:)
6060

61+
const >: = Core.:(>:)
62+
6163
"""
6264
>:(T1, T2)
6365
6466
Supertype operator, equivalent to `T2 <: T1`.
6567
"""
66-
(>:)(@nospecialize(a), @nospecialize(b)) = (b <: a)
68+
>:
6769

6870
"""
6971
supertype(T::Union{DataType, UnionAll})

0 commit comments

Comments
 (0)