diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 89709cb29960b..3d6f35c2e335b 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -666,7 +666,7 @@ function _pure_eval_call(@nospecialize(f), arginfo::ArgInfo) args = collect_const_args(arginfo) try value = Core._apply_pure(f, args) - return Const(value) + return mkConst(value) catch return nothing end @@ -684,7 +684,7 @@ end function is_all_const_arg((; argtypes)::ArgInfo) for i = 2:length(argtypes) a = widenconditional(argtypes[i]) - isa(a, Const) || isconstType(a) || issingletontype(a) || return false + isa(a, Const) || isa(a, ConstType) || isconstType(a) || issingletontype(a) || return false end return true end @@ -692,6 +692,7 @@ end function collect_const_args((; argtypes)::ArgInfo) return Any[ let a = widenconditional(argtypes[i]) isa(a, Const) ? a.val : + isa(a, ConstType) ? a.val : isconstType(a) ? (a::DataType).parameters[1] : (a::DataType).instance end for i in 2:length(argtypes) ] @@ -707,7 +708,7 @@ function concrete_eval_call(interp::AbstractInterpreter, # If the constant is not inlineable, still do the const-prop, since the # code that led to the creation of the Const may be inlineable in the same # circumstance and may be optimizable. - return ConstCallResults(Const(value), ConstResult(result.edge, value), EFFECTS_TOTAL) + return ConstCallResults(mkConst(value), ConstResult(result.edge, value), EFFECTS_TOTAL) end catch # The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime @@ -877,6 +878,7 @@ function is_const_prop_profitable_arg(@nospecialize(arg)) end end isa(arg, PartialOpaque) && return true + isa(arg, ConstType) && return true isa(arg, Const) || return true val = arg.val # don't consider mutable values or Strings useful constants @@ -1044,7 +1046,7 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) if isa(typ, Const) val = typ.val if isa(val, SimpleVector) || isa(val, Tuple) - return Any[ Const(val[i]) for i in 1:length(val) ], nothing # avoid making a tuple Generator here! + return Any[ mkConst(val[i]) for i in 1:length(val) ], nothing # avoid making a tuple Generator here! end end @@ -1102,7 +1104,7 @@ end # simulate iteration protocol on container type up to fixpoint function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @nospecialize(itertype), sv::InferenceState) - if isa(itft, Const) + if isConst(itft) iteratef = itft.val else return Any[Vararg{Any}], nothing @@ -1142,7 +1144,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n valtype = getfield_tfunc(stateordonet, Const(1)) push!(ret, valtype) statetype = nstatetype - call = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[Const(iteratef), itertype, statetype]), sv) + call = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[mkConst(iteratef), itertype, statetype]), sv) stateordonet = call.rt stateordonet_widened = widenconst(stateordonet) push!(calls, call) @@ -1177,7 +1179,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n end valtype = tmerge(valtype, nounion.parameters[1]) statetype = tmerge(statetype, nounion.parameters[2]) - stateordonet = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[Const(iteratef), itertype, statetype]), sv).rt + stateordonet = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[mkConst(iteratef), itertype, statetype]), sv).rt stateordonet_widened = widenconst(stateordonet) end if valtype !== Union{} @@ -1194,7 +1196,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv:: (itft === Bottom || aft === Bottom) && return CallMeta(Bottom, false) aargtypes = argtype_tail(argtypes, 4) aftw = widenconst(aft) - if !isa(aft, Const) && !isa(aft, PartialOpaque) && (!isType(aftw) || has_free_typevars(aftw)) + if !isConst(aft) && !isa(aft, PartialOpaque) && (!isType(aftw) || has_free_typevars(aftw)) if !isconcretetype(aftw) || (aftw <: Builtin) add_remark!(interp, sv, "Core._apply_iterate called on a function of a non-concrete type") tristate_merge!(sv, Effects()) @@ -1357,7 +1359,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs aty = argtypes[2] bty = argtypes[3] # if doing a comparison to a singleton, consider returning a `Conditional` instead - if isa(aty, Const) && isa(b, SlotNumber) + if isConst(aty) && isa(b, SlotNumber) if rt === Const(false) aty = Union{} elseif rt === Const(true) @@ -1367,7 +1369,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs end return Conditional(b, aty, bty) end - if isa(bty, Const) && isa(a, SlotNumber) + if isConst(bty) && isa(a, SlotNumber) if rt === Const(false) bty = Union{} elseif rt === Const(true) @@ -1428,7 +1430,7 @@ function abstract_call_unionall(argtypes::Vector{Any}) if length(argtypes) == 3 canconst = true a3 = argtypes[3] - if isa(a3, Const) + if isConst(a3) body = a3.val elseif isType(a3) body = a3.parameters[1] @@ -1452,7 +1454,7 @@ function abstract_call_unionall(argtypes::Vector{Any}) !isa(tv, TypeVar) && return Any body = UnionAll(tv, body) end - ret = canconst ? Const(body) : Type{body} + ret = canconst ? ConstType(body) : Type{body} return ret end return Any @@ -1558,8 +1560,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), tristate_merge!(sv, Effects()) # TODO (la < 2 || la > 4) && return CallMeta(Union{}, false) n = argtypes[2] - ub_var = Const(Any) - lb_var = Const(Union{}) + ub_var = ConstType(Any, Type{Any}) + lb_var = ConstType(Union{}, Type{Union{}}) if la == 4 ub_var = argtypes[4] lb_var = argtypes[3] @@ -1617,7 +1619,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), istopfunction(f, :getindex) # mark getindex(::SimpleVector, i::Int) as @pure if 1 <= idx <= length(svecval) && isassigned(svecval, idx) - return CallMeta(Const(getindex(svecval, idx)), MethodResultPure()) + return CallMeta(mkConst(getindex(svecval, idx)), MethodResultPure()) end elseif la == 2 && istopfunction(f, :typename) return CallMeta(typename_static(argtypes[2]), MethodResultPure()) @@ -1756,7 +1758,7 @@ end function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) if isa(e, QuoteNode) - return Const(e.value) + return mkConst(e.value) elseif isa(e, SSAValue) return abstract_eval_ssavalue(e, sv) elseif isa(e, SlotNumber) || isa(e, Argument) @@ -1765,7 +1767,7 @@ function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize( return abstract_eval_global(e.mod, e.name, sv) end - return Const(e) + return mkConst(e) end function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) @@ -1834,7 +1836,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), ALWAYS_TRUE, # N.B depends on !ismutabletype(t) above ALWAYS_TRUE, ALWAYS_FALSE, ALWAYS_TRUE)) @goto t_computed - elseif !isa(at, Const) + elseif !isConst(at) allconst = false end if !anyrefine @@ -1850,7 +1852,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), if allconst argvals = Vector{Any}(undef, nargs) for j in 1:nargs - argvals[j] = (ats[j]::Const).val + argvals[j] = (ats[j]::Union{Const, ConstType}).val end t = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), t, argvals, nargs)) elseif anyrefine @@ -1964,7 +1966,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), n = sym.args[1]::Int if 1 <= n <= length(sv.sptypes) spty = sv.sptypes[n] - if isa(spty, Const) + if isConst(spty) t = Const(true) end end @@ -1974,12 +1976,9 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), end @label t_computed @assert !isa(t, TypeVar) "unhandled TypeVar" - if isa(t, DataType) && isdefined(t, :instance) - # replace singleton types with their equivalent Const object - t = Const(t.instance) - end + t = maybe_singleton_const(t) if !isempty(sv.pclimitations) - if t isa Const || t === Union{} + if t isa Const || t isa ConstType || t === Union{} empty!(sv.pclimitations) else t = LimitedAccuracy(t, sv.pclimitations) @@ -1992,7 +1991,19 @@ end function abstract_eval_global(M::Module, s::Symbol) if isdefined(M,s) if isconst(M,s) - return Const(getfield(M,s)) + val = getfield(M,s) + if isa(val, Type) + ty = ccall(:jl_binding_type, Any, (Any, Any), M, s) + if ty !== nothing && isa(ty, DataType) && ty.name === Type.body.name + # Make sure this is actually a Type{...}. The frontend inserts + # that, but in theory nothing prevents the user from creating + # their own constant binding with arbitrary binding type. + return ConstType(val, ty) + end + return ConstType(val) + else + return Const(val) + end end end ty = ccall(:jl_binding_type, Any, (Any, Any), M, s) @@ -2002,7 +2013,7 @@ end function abstract_eval_global(M::Module, s::Symbol, frame::InferenceState) ty = abstract_eval_global(M, s) - isa(ty, Const) && return ty + isConst(ty) && return ty if isdefined(M,s) tristate_merge!(frame, Effects(EFFECTS_TOTAL, consistent=ALWAYS_FALSE)) else @@ -2072,6 +2083,7 @@ function widenreturn(@nospecialize(rt), @nospecialize(bestguess), nslots::Int, s end function widenreturn_noconditional(@nospecialize(rt)) + isa(rt, ConstType) && return rt isa(rt, Const) && return rt isa(rt, Type) && return rt if isa(rt, PartialStruct) diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl index 8e3d3d5917fd0..6bed177fa8262 100644 --- a/base/compiler/inferenceresult.jl +++ b/base/compiler/inferenceresult.jl @@ -11,6 +11,7 @@ end function is_forwardable_argtype(@nospecialize x) return isa(x, Const) || + isa(x, ConstType) || isa(x, Conditional) || isa(x, PartialStruct) || isa(x, PartialOpaque) @@ -130,13 +131,7 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe push!(vargtype_elements, elim_free_typevars(rewrap_unionall(p, specTypes))) end for i in 1:length(vargtype_elements) - atyp = vargtype_elements[i] - if isa(atyp, DataType) && isdefined(atyp, :instance) - # replace singleton types with their equivalent Const object - vargtype_elements[i] = Const(atyp.instance) - elseif isconstType(atyp) - vargtype_elements[i] = Const(atyp.parameters[1]) - end + vargtype_elements[i] = maybe_singleton_const(vargtype_elements[i]) end vargtype = tuple_tfunc(vargtype_elements) end @@ -161,10 +156,13 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe end atyp = unwraptv(atyp) if isa(atyp, DataType) && isdefined(atyp, :instance) - # replace singleton types with their equivalent Const object - atyp = Const(atyp.instance) + if atyp === typeof(Bottom) + atyp = ConstType(Bottom) + else + atyp = Const(atyp.instance) + end elseif isconstType(atyp) - atyp = Const(atyp.parameters[1]) + atyp = ConstType(atyp.parameters[1], atyp) else atyp = elim_free_typevars(rewrap_unionall(atyp, specTypes)) end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 928811dd63a3b..4722df0159254 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -331,7 +331,7 @@ function sptypes_from_meth_instance(linfo::MethodInstance) elseif isvarargtype(v) ty = Int else - ty = Const(v) + ty = mkConst(v) end sp[i] = ty end diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 0616204dce748..409cb288e6cc8 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -196,7 +196,7 @@ function stmt_effect_free(@nospecialize(stmt), @nospecialize(rt), src::Union{IRC if head === :static_parameter etyp = (isa(src, IRCode) ? src.sptypes : src.ir.sptypes)[args[1]::Int] # if we aren't certain enough about the type, it might be an UndefVarError at runtime - return isa(etyp, Const) + return isConst(etyp) end if head === :call f = argextype(args[1], src) @@ -361,7 +361,7 @@ function argextype( elseif isa(x, Argument) return slottypes[x.n] elseif isa(x, QuoteNode) - return Const(x.value) + return mkConst(x.value) elseif isa(x, GlobalRef) return abstract_eval_global(x.mod, x.name) elseif isa(x, PhiNode) @@ -369,7 +369,7 @@ function argextype( elseif isa(x, PiNode) return x.typ else - return Const(x) + return mkConst(x) end end abstract_eval_ssavalue(s::SSAValue, src::Union{IRCode,IncrementalCompact}) = types(src)[s] @@ -402,7 +402,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState, result = caller.result @assert !(result isa LimitedAccuracy) result = isa(result, InterConditional) ? widenconditional(result) : result - if (isa(result, Const) || isconstType(result)) + if (isConst(result) || isconstType(result)) proven_pure = false # must be proven pure to use constant calling convention; # otherwise we might skip throwing errors (issue #20704) @@ -436,7 +436,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState, # Still set pure flag to make sure `inference` tests pass # and to possibly enable more optimization in the future src.pure = true - if isa(result, Const) + if isConst(result) val = result.val if is_inlineable_constant(val) analyzed = ConstAPI(val) @@ -644,7 +644,7 @@ end plus_saturate(x::Int, y::Int) = max(x, y, x+y) # known return type -isknowntype(@nospecialize T) = (T === Union{}) || isa(T, Const) || isconcretetype(widenconst(T)) +isknowntype(@nospecialize T) = (T === Union{}) || isa(T, Const) || isa(T, ConstType) || isconcretetype(widenconst(T)) function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{Any}, union_penalties::Bool, params::OptimizationParams, error_path::Bool = false) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 17141cf8eaec7..6d918ba28af95 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -661,7 +661,7 @@ function rewrite_apply_exprargs!( def_argtypes = Any[] if isa(def_type, Const) # && isa(def_type.val, Union{Tuple, SimpleVector}) is implied for p in def_type.val - push!(def_argtypes, Const(p)) + push!(def_argtypes, mkConst(p)) end else ti = widenconst(def_type)::DataType # checked by `is_valid_type_for_apply_rewrite` @@ -669,20 +669,14 @@ function rewrite_apply_exprargs!( ti = ti.parameters[2]::DataType # checked by `is_valid_type_for_apply_rewrite` end for p in ti.parameters - if isa(p, DataType) && isdefined(p, :instance) - # replace singleton types with their equivalent Const object - p = Const(p.instance) - elseif isconstType(p) - p = Const(p.parameters[1]) - end - push!(def_argtypes, p) + push!(def_argtypes, maybe_singleton_const(p)) end end end # now push flattened types into new_argtypes and getfield exprs into new_argexprs for j in 1:length(def_argtypes) def_atype = def_argtypes[j] - if isa(def_atype, Const) && is_inlineable_constant(def_atype.val) + if isConst(def_atype) && is_inlineable_constant(def_atype.val) new_argexpr = quoted(def_atype.val) else new_call = Expr(:call, GlobalRef(Core, :getfield), def, j) @@ -1369,7 +1363,7 @@ end function inline_const_if_inlineable!(inst::Instruction) rt = inst[:type] - if rt isa Const && is_inlineable_constant(rt.val) + if isConst(rt) && is_inlineable_constant(rt.val) inst[:inst] = quoted(rt.val) return true end @@ -1471,7 +1465,7 @@ function early_inline_special_case( params.inlining || return nothing (; f, ft, argtypes) = sig - if isa(type, Const) # || isconstType(type) + if isConst(type) # || isconstType(type) val = type.val is_inlineable_constant(val) || return nothing if isa(f, IntrinsicFunction) @@ -1505,7 +1499,7 @@ function late_inline_special_case!( if length(argtypes) == 3 && istopfunction(f, :!==) # special-case inliner for !== that precedes _methods_by_ftype union splitting # and that works, even though inference generally avoids inferring the `!==` Method - if isa(type, Const) + if isConst(type) return SomeCase(quoted(type.val)) end cmp_call = Expr(:call, GlobalRef(Core, :(===)), stmt.args[2], stmt.args[3]) @@ -1515,7 +1509,7 @@ function late_inline_special_case!( elseif length(argtypes) == 3 && istopfunction(f, :(>:)) # special-case inliner for issupertype # that works, even though inference generally avoids inferring the `>:` Method - if isa(type, Const) && _builtin_nothrow(<:, Any[argtypes[3], argtypes[2]], type) + if isConst(type) && _builtin_nothrow(<:, Any[argtypes[3], argtypes[2]], type) return SomeCase(quoted(type.val)) end subtype_call = Expr(:call, GlobalRef(Core, :(<:)), stmt.args[3], stmt.args[2]) @@ -1532,7 +1526,7 @@ function late_inline_special_case!( elseif is_return_type(f) if isconstType(type) return SomeCase(quoted(type.parameters[1])) - elseif isa(type, Const) + elseif isConst(type) return SomeCase(quoted(type.val)) end end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index fdc50b3b481cd..25bbb71db3a35 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -41,7 +41,7 @@ function try_compute_field(ir::Union{IncrementalCompact,IRCode}, @nospecialize(f else # try to resolve other constants, e.g. global reference field = argextype(field, ir) - if isa(field, Const) + if isConst(field) field = field.val else return nothing @@ -400,7 +400,7 @@ function lift_leaves(compact::IncrementalCompact, return nothing else typ = argextype(leaf, compact) - if !isa(typ, Const) + if !isConst(typ) # TODO: (disabled since #27126) # If the leaf is an old ssa value, insert a getfield here # We will revisit this getfield later when compaction gets @@ -495,11 +495,11 @@ function lift_comparison!(::typeof(===), compact::IncrementalCompact, lhs, rhs = args[2], args[3] vl = argextype(lhs, compact) vr = argextype(rhs, compact) - if isa(vl, Const) - isa(vr, Const) && return + if isConst(vl) + isConst(vr) && return val = rhs cmp = vl - elseif isa(vr, Const) + elseif isConst(vr) val = lhs cmp = vr else @@ -522,7 +522,7 @@ function lift_comparison!(::typeof(isdefined), compact::IncrementalCompact, args = stmt.args length(args) == 3 || return cmp = argextype(args[3], compact) - isa(cmp, Const) || return # `isdefined_tfunc` won't return Const + isConst(cmp) || return # `isdefined_tfunc` won't return Const val = args[2] lift_comparison_leaves!(isdefined_tfunc, compact, val, cmp, lifting_cache, idx) end @@ -543,7 +543,7 @@ function lift_comparison_leaves!(@specialize(tfunc), for i = 1:length(leaves) leaf = leaves[i] result = tfunc(argextype(leaf, compact), cmp) - if isa(result, Const) + if isConst(result) if lifted_leaves === nothing lifted_leaves = LiftedLeaves() end diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 3317db8b55e0b..aab99d7de102c 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -221,9 +221,9 @@ function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{Any}, idx:: isa(x, SSAValue) && return (ci.ssavaluetypes::Vector{Any})[x.id] isa(x, Argument) && return slottypes[x.n] isa(x, NewSSAValue) && return DelayedTyp(x) - isa(x, QuoteNode) && return Const(x.value) + isa(x, QuoteNode) && return mkConst(x.value) isa(x, Union{Symbol, PiNode, PhiNode, SlotNumber, TypedSlot}) && error("unexpected val type") - return Const(x) + return mkConst(x) end struct BlockLiveness diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 54b89c9a04f43..549091eb80ab2 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -54,7 +54,7 @@ add_tfunc(throw, 1, 1, (@nospecialize(x)) -> Bottom, 0) # if isconcrete is true, the actual runtime type is definitely concrete (unreachable if not valid as a typeof) # if istype is true, the actual runtime value will definitely be a type (e.g. this is false for Union{Type{Int}, Int}) function instanceof_tfunc(@nospecialize(t)) - if isa(t, Const) + if isConst(t) if isa(t.val, Type) && valid_as_lattice(t.val) return t.val, true, isconcretetype(t.val), true end @@ -200,7 +200,8 @@ add_tfunc(Core.Intrinsics.llvmcall, 3, INT_INF, (@nospecialize(fptr), @nospecialize(rt), @nospecialize(at), a...) -> instanceof_tfunc(rt)[1], 10) cglobal_tfunc(@nospecialize(fptr)) = Ptr{Cvoid} cglobal_tfunc(@nospecialize(fptr), @nospecialize(t)) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) -cglobal_tfunc(@nospecialize(fptr), t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) +cglobal_tfunc(@nospecialize(fptr), t::ConstType) = Ptr{t.val} +cglobal_tfunc(@nospecialize(fptr), t::Const) = Ptr add_tfunc(Core.Intrinsics.cglobal, 1, 2, cglobal_tfunc, 5) add_tfunc(Core.Intrinsics.have_fma, 1, 1, @nospecialize(x)->Bool, 1) @@ -225,20 +226,20 @@ add_tfunc(Core.ifelse, 3, 3, ifelse_tfunc, 1) function egal_tfunc(@nospecialize(x), @nospecialize(y)) xx = widenconditional(x) yy = widenconditional(y) - if isa(x, Conditional) && isa(yy, Const) + if isa(x, Conditional) && isConst(yy) yy.val === false && return Conditional(x.var, x.elsetype, x.vtype) yy.val === true && return x return Const(false) - elseif isa(y, Conditional) && isa(xx, Const) + elseif isa(y, Conditional) && isConst(xx) xx.val === false && return Conditional(y.var, y.elsetype, y.vtype) xx.val === true && return y return Const(false) - elseif isa(xx, Const) && isa(yy, Const) + elseif isConst(xx) && isConst(yy) return Const(xx.val === yy.val) elseif !hasintersect(widenconst(xx), widenconst(yy)) return Const(false) - elseif (isa(xx, Const) && y === typeof(xx.val) && isdefined(y, :instance)) || - (isa(yy, Const) && x === typeof(yy.val) && isdefined(x, :instance)) + elseif (isConst(xx) && y === typeof(xx.val) && isdefined(y, :instance)) || + (isConst(yy) && x === typeof(yy.val) && isdefined(x, :instance)) return Const(true) end return Bool @@ -253,7 +254,7 @@ function isdefined_nothrow(argtypes::Array{Any, 1}) end isdefined_tfunc(arg1, sym, order) = (@nospecialize; isdefined_tfunc(arg1, sym)) function isdefined_tfunc(@nospecialize(arg1), @nospecialize(sym)) - if isa(arg1, Const) + if isConst(arg1) a1 = typeof(arg1.val) else a1 = widenconst(arg1) @@ -291,8 +292,8 @@ function isdefined_tfunc(@nospecialize(arg1), @nospecialize(sym)) end elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) return Const(false) - elseif isa(arg1, Const) - arg1v = (arg1::Const).val + elseif isConst(arg1) + arg1v = arg1.val if !ismutable(arg1v) || isdefined(arg1v, idx) || isconst(typeof(arg1v), idx) return Const(isdefined(arg1v, idx)) end @@ -312,7 +313,7 @@ end add_tfunc(isdefined, 2, 3, isdefined_tfunc, 1) function sizeof_nothrow(@nospecialize(x)) - if isa(x, Const) + if isConst(x) if !isa(x.val, Type) || x.val === DataType return true end @@ -365,7 +366,7 @@ function _const_sizeof(@nospecialize(x)) return Const(size) end function sizeof_tfunc(@nospecialize(x),) - isa(x, Const) && return _const_sizeof(x.val) + isConst(x) && return _const_sizeof(x.val) isa(x, Conditional) && return _const_sizeof(Bool) isconstType(x) && return _const_sizeof(x.parameters[1]) xu = unwrap_unionall(x) @@ -395,7 +396,7 @@ function sizeof_tfunc(@nospecialize(x),) end add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 1) function nfields_tfunc(@nospecialize(x)) - isa(x, Const) && return Const(nfields(x.val)) + isConst(x) && return Const(nfields(x.val)) isa(x, Conditional) && return Const(0) x = unwrap_unionall(widenconst(x)) isconstType(x) && return Const(nfields(x.parameters[1])) @@ -422,7 +423,7 @@ function typevar_tfunc(@nospecialize(n), @nospecialize(lb_arg), @nospecialize(ub if isa(n, Const) nval = n.val isa(nval, Symbol) || return Union{} - if isa(lb_arg, Const) + if isConst(lb_arg) lb = lb_arg.val elseif isType(lb_arg) lb = lb_arg.parameters[1] @@ -430,7 +431,7 @@ function typevar_tfunc(@nospecialize(n), @nospecialize(lb_arg), @nospecialize(ub else return TypeVar end - if isa(ub_arg, Const) + if isConst(ub_arg) ub = ub_arg.val elseif isType(ub_arg) ub = ub_arg.parameters[1] @@ -546,16 +547,16 @@ function typeof_concrete_vararg(t::DataType) end function typeof_tfunc(@nospecialize(t)) - isa(t, Const) && return Const(typeof(t.val)) + isConst(t) && return ConstType(typeof(t.val)) t = widenconst(t) if isType(t) tp = t.parameters[1] if hasuniquerep(tp) - return Const(typeof(tp)) + return ConstType(typeof(tp)) end elseif isa(t, DataType) if isconcretetype(t) - return Const(t) + return ConstType(t) elseif t === Any return DataType else @@ -615,7 +616,7 @@ function isa_tfunc(@nospecialize(v), @nospecialize(tt)) return Const(true) end else - if isa(v, Const) || isa(v, Conditional) + if isConst(v) || isa(v, Conditional) # this and the `isdispatchelem` below test for knowledge of a # leaftype appearing on the LHS (ensuring the isa is precise) return Const(false) @@ -735,8 +736,8 @@ function getfield_nothrow(@nospecialize(s00), @nospecialize(name), boundscheck:: end # If we have s00 being a const, we can potentially refine our type-based analysis above - if isa(s00, Const) || isconstType(s00) - if !isa(s00, Const) + if isConst(s00) || isconstType(s00) + if !isConst(s00) sv = s00.parameters[1] else sv = s00.val @@ -808,8 +809,8 @@ getfield_tfunc(@nospecialize(s00), @nospecialize(name)) = _getfield_tfunc(s00, n function _getfield_tfunc(@nospecialize(s00), @nospecialize(name), setfield::Bool) if isa(s00, Conditional) return Bottom # Bool has no fields - elseif isa(s00, Const) || isconstType(s00) - if !isa(s00, Const) + elseif isConst(s00) || isconstType(s00) + if !isConst(s00) sv = s00.parameters[1] else sv = s00.val @@ -830,11 +831,11 @@ function _getfield_tfunc(@nospecialize(s00), @nospecialize(name), setfield::Bool return Bottom end if isa(sv, DataType) && nv == DATATYPE_TYPES_FIELDINDEX && isdefined(sv, nv) - return Const(getfield(sv, nv)) + return mkConst(getfield(sv, nv)) end if isconst(typeof(sv), nv) if isdefined(sv, nv) - return Const(getfield(sv, nv)) + return mkConst(getfield(sv, nv)) end return Union{} end @@ -1004,7 +1005,7 @@ function modifyfield!_tfunc(o, f, op, v) @nospecialize T = _fieldtype_tfunc(o, isconcretetype(o), f) T === Bottom && return Bottom - PT = Const(Pair) + PT = ConstType(Pair) return instanceof_tfunc(apply_type_tfunc(PT, T, T))[1] end function abstract_modifyfield!(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState) @@ -1044,7 +1045,7 @@ function replacefield!_tfunc(o, f, x, v) @nospecialize T = _fieldtype_tfunc(o, isconcretetype(o), f) T === Bottom && return Bottom - PT = Const(ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T) + PT = mkConst(ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T) return instanceof_tfunc(apply_type_tfunc(PT, T))[1] end @@ -1125,7 +1126,7 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) return Any end # fieldtype only accepts Types - if isa(s0, Const) && !(isa(s0.val, DataType) || isa(s0.val, UnionAll) || isa(s0.val, Union)) + if isConst(s0) && !(isa(s0.val, DataType) || isa(s0.val, UnionAll) || isa(s0.val, Union)) return Bottom end if (s0 isa Type && s0 == Type{Union{}}) || isa(s0, Conditional) @@ -1154,7 +1155,7 @@ function _fieldtype_tfunc(@nospecialize(s), exact::Bool, @nospecialize(name)) ta, exacta, _, istypea = instanceof_tfunc(ta0) tb, exactb, _, istypeb = instanceof_tfunc(tb0) if exact && exacta && exactb - return Const(Union{ta, tb}) + return mkConst(Union{ta, tb}) end if istypea && istypeb return Type{<:Union{ta, tb}} @@ -1191,7 +1192,7 @@ function _fieldtype_tfunc(@nospecialize(s), exact::Bool, @nospecialize(name)) ft1 = rewrap_unionall(ft1, s) if exactft1 if hasuniquerep(ft1) - ft1 = Const(ft1) # ft unique via type cache + ft1 = ConstType(ft1) # ft unique via type cache else ft1 = Type{ft1} end @@ -1234,7 +1235,7 @@ function _fieldtype_tfunc(@nospecialize(s), exact::Bool, @nospecialize(name)) ft = rewrap_unionall(ft, s) if exactft if hasuniquerep(ft) - return Const(ft) # ft unique via type cache + return ConstType(ft) # ft unique via type cache end return Type{ft} end @@ -1266,7 +1267,7 @@ function apply_type_nothrow(argtypes::Array{Any, 1}, @nospecialize(rt)) rt === Type && return false length(argtypes) >= 1 || return false headtypetype = argtypes[1] - if isa(headtypetype, Const) + if isConst(headtypetype) headtype = headtypetype.val elseif isconstType(headtypetype) headtype = headtypetype.parameters[1] @@ -1276,7 +1277,7 @@ function apply_type_nothrow(argtypes::Array{Any, 1}, @nospecialize(rt)) # We know the apply_type is well formed. Otherwise our rt would have been # Bottom (or Type). (headtype === Union) && return true - isa(rt, Const) && return true + isConst(rt) && return true u = headtype for i = 2:length(argtypes) isa(u, UnionAll) || return false @@ -1287,8 +1288,8 @@ function apply_type_nothrow(argtypes::Array{Any, 1}, @nospecialize(rt)) if !(u.var.lb === Union{} && u.var.ub === Any) return false end - elseif (isa(ai, Const) && isa(ai.val, Type)) || isconstType(ai) - ai = isa(ai, Const) ? ai.val : (ai::DataType).parameters[1] + elseif (isConst(ai) && isa(ai.val, Type)) || isconstType(ai) + ai = isConst(ai) ? ai.val : (ai::DataType).parameters[1] if has_free_typevars(u.var.lb) || has_free_typevars(u.var.ub) return false end @@ -1324,7 +1325,7 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, # TODO: handle e.g. apply_type(T, R::Union{Type{Int32},Type{Float64}}) function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) - if isa(headtypetype, Const) + if isConst(headtypetype) headtype = headtypetype.val elseif isconstType(headtypetype) headtype = headtypetype.parameters[1] @@ -1336,11 +1337,11 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) end largs = length(args) if headtype === Union - largs == 0 && return Const(Bottom) + largs == 0 && return ConstType(Bottom) hasnonType = false for i = 1:largs ai = args[i] - if isa(ai, Const) + if isConst(ai) if !isa(ai.val, Type) if isa(ai.val, TypeVar) hasnonType = true @@ -1372,11 +1373,11 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) aty = ai.parameters[1] allconst &= hasuniquerep(aty) else - aty = (ai::Const).val + aty = (ai::Union{Const, ConstType}).val end ty = Union{ty, aty} end - return allconst ? Const(ty) : Type{ty} + return allconst ? ConstType(ty) : Type{ty} end istuple = isa(headtype, Type) && (headtype == Tuple) if !istuple && !isa(headtype, UnionAll) && !isvarargtype(headtype) @@ -1395,7 +1396,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) aip1 = ai.parameters[1] canconst &= !has_free_typevars(aip1) push!(tparams, aip1) - elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || + elseif isConst(ai) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val) || (istuple && isvarargtype(ai.val))) push!(tparams, ai.val) elseif isa(ai, PartialTypeVar) @@ -1464,7 +1465,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) # doesn't match, which could happen if a type estimate is too coarse return isvarargtype(headtype) ? TypeofVararg : Type{<:headtype} end - !uncertain && canconst && return Const(appl) + !uncertain && canconst && return isvarargtype(appl) ? Const(appl) : ConstType(appl) if isvarargtype(appl) return TypeofVararg end @@ -1491,7 +1492,7 @@ function tuple_tfunc(argtypes::Vector{Any}) argtypes = anymap(widenconditional, argtypes) all_are_const = true for i in 1:length(argtypes) - if !isa(argtypes[i], Const) + if !isConst(argtypes[i]) all_are_const = false break end @@ -1511,7 +1512,7 @@ function tuple_tfunc(argtypes::Vector{Any}) end argtypes[i] = x end - if isa(x, Const) + if isConst(x) params[i] = typeof(x.val) else x = isvarargtype(x) ? x : widenconst(x) @@ -1692,7 +1693,7 @@ function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecializ return sizeof_nothrow(argtypes[1]) elseif f === Core.kwfunc length(argtypes) == 1 || return false - return isa(rt, Const) + return isConst(rt) elseif f === Core.ifelse length(argtypes) == 3 || return false return argtypes[1] ⊑ Bool @@ -1700,7 +1701,7 @@ function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecializ length(argtypes) == 2 || return false a3 = argtypes[2] if (isType(a3) && !has_free_typevars(a3) && argtypes[1] ⊑ a3.parameters[1]) || - (isa(a3, Const) && isa(a3.val, Type) && argtypes[1] ⊑ a3.val) + (isConst(a3) && isa(a3.val, Type) && argtypes[1] ⊑ a3.val) return true end return false @@ -1804,8 +1805,8 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp return tuple_tfunc(argtypes) end if isa(f, IntrinsicFunction) - if is_pure_intrinsic_infer(f) && _all(@nospecialize(a) -> isa(a, Const), argtypes) - argvals = anymap(@nospecialize(a) -> (a::Const).val, argtypes) + if is_pure_intrinsic_infer(f) && _all(@nospecialize(a) -> isConst(a), argtypes) + argvals = anymap(@nospecialize(a) -> (a::Union{Const, ConstType}).val, argtypes) try return Const(f(argvals...)) catch @@ -1979,37 +1980,37 @@ end function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState) if length(argtypes) == 3 tt = argtypes[3] - if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) + if isConst(tt) || (isType(tt) && !has_free_typevars(tt)) aft = argtypes[2] - if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || + if isConst(aft) || (isType(aft) && !has_free_typevars(aft)) || (isconcretetype(aft) && !(aft <: Builtin)) - af_argtype = isa(tt, Const) ? tt.val : (tt::DataType).parameters[1] + af_argtype = isConst(tt) ? tt.val : (tt::DataType).parameters[1] if isa(af_argtype, DataType) && af_argtype <: Tuple argtypes_vec = Any[aft, af_argtype.parameters...] if contains_is(argtypes_vec, Union{}) - return CallMeta(Const(Union{}), false) + return CallMeta(ConstType(Union{}), false) end call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), sv, -1) info = verbose_stmt_info(interp) ? ReturnTypeCallInfo(call.info) : false rt = widenconditional(call.rt) - if isa(rt, Const) + if isConst(rt) # output was computed to be constant - return CallMeta(Const(typeof(rt.val)), info) + return CallMeta(ConstType(typeof(rt.val)), info) end rt = widenconst(rt) if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) # output cannot be improved so it is known for certain - return CallMeta(Const(rt), info) + return CallMeta(ConstType(rt), info) elseif !isempty(sv.pclimitations) # conservatively express uncertainty of this result # in two ways: both as being a subtype of this, and # because of LimitedAccuracy causes return CallMeta(Type{<:rt}, info) - elseif (isa(tt, Const) || isconstType(tt)) && - (isa(aft, Const) || isconstType(aft)) + elseif (isConst(tt) || isconstType(tt)) && + (isConst(aft) || isconstType(aft)) # input arguments were known for certain # XXX: this doesn't imply we know anything about rt - return CallMeta(Const(rt), info) + return CallMeta(ConstType(rt), info) elseif isType(rt) return CallMeta(Type{rt}, info) else @@ -2040,7 +2041,7 @@ end function get_binding_type_tfunc(@nospecialize(M), @nospecialize(s)) if get_binding_type_effect_free(M, s) @assert M isa Const && s isa Const - return Const(Core.get_binding_type(M.val, s.val)) + return ConstType(Core.get_binding_type(M.val, s.val)) end return Type end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 4015b7c00bf0d..85505e1a673e7 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -220,7 +220,10 @@ function finish!(interp::AbstractInterpreter, caller::InferenceResult) opt = caller.src if opt isa OptimizationState # implies `may_optimize(interp) === true` if opt.ir !== nothing + # Already done in the cache caller.src = ir_to_codeinf!(opt) + else + caller.src = opt.src end end return caller.src @@ -292,7 +295,7 @@ function CodeInstance( const_flags = 0x3 inferred_result = nothing else - if isa(result_type, Const) + if isConst(result_type) rettype_const = result_type.val const_flags = 0x2 elseif isa(result_type, PartialOpaque) @@ -831,7 +834,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize elseif isa(rettype_const, InterConditional) && !(InterConditional <: rettype) return rettype_const, mi, effects else - return Const(rettype_const), mi, effects + return mkConst(rettype_const), mi, effects end else return rettype, mi, effects diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index bba9f41bf64d3..1de077538727f 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -18,6 +18,25 @@ # end import Core: Const, PartialStruct +# Like `Const`, but `widencost` widens to Type{...} rather than typeof +mutable struct ConstType + const val # ::Type + Typeof::DataType + ConstType(@nospecialize(val)) = new(val) + ConstType(@nospecialize(val), Typeof::DataType) = new(val, Typeof) +end +==(a::ConstType, b::ConstType) = a.val === b.val + +function mkConst(@nospecialize(v)) + if isa(v, Type) + return ConstType(v) + else + return Const(v) + end +end + +isConst(@nospecialize(v)) = isa(v, Union{Const, ConstType}) + # The type of this value might be Bool. # However, to enable a limited amount of back-propagation, # we also keep some information about how this Bool value was created. @@ -105,7 +124,7 @@ struct NotFound end const NOT_FOUND = NotFound() -const CompilerTypes = Union{MaybeUndef, Const, Conditional, NotFound, PartialStruct} +const CompilerTypes = Union{MaybeUndef, Const, ConstType, Conditional, NotFound, PartialStruct} ==(x::CompilerTypes, y::CompilerTypes) = x === y ==(x::Type, y::CompilerTypes) = false ==(x::CompilerTypes, y::Type) = false @@ -190,7 +209,7 @@ The non-strict partial order over the type inference lattice. end return isa(b, Type) && a.typ <: b elseif isa(b, PartialStruct) - if isa(a, Const) + if isConst(a) nfields(a.val) == length(b.fields) || return false widenconst(b).name === widenconst(a).name || return false # We can skip the subtype check if b is a Tuple, since in that @@ -201,7 +220,7 @@ The non-strict partial order over the type inference lattice. for i in 1:nfields(a.val) # XXX: let's handle varargs later isdefined(a.val, i) || return false - ⊑(Const(getfield(a.val, i)), b.fields[i]) || return false + ⊑(mkConst(getfield(a.val, i)), b.fields[i]) || return false end return true end @@ -215,15 +234,15 @@ The non-strict partial order over the type inference lattice. end return widenconst(a) ⊑ b end - if isa(a, Const) - if isa(b, Const) + if isConst(a) + if isConst(b) return a.val === b.val end # TODO: `b` could potentially be a `PartialTypeVar` here, in which case we might be # able to return `true` in more cases; in the meantime, just returning this is the # most conservative option. return isa(b, Type) && isa(a.val, b) - elseif isa(b, Const) + elseif isConst(b) if isa(a, DataType) && isdefined(a, :instance) return a.instance === b.val end @@ -267,13 +286,13 @@ function is_lattice_equal(@nospecialize(a), @nospecialize(b)) return true end isa(b, PartialStruct) && return false - if a isa Const + if isConst(a) if issingletontype(b) return a.val === b.instance end return false end - if b isa Const + if isConst(b) if issingletontype(a) return a.instance === b.val end @@ -290,7 +309,13 @@ function is_lattice_equal(@nospecialize(a), @nospecialize(b)) end widenconst(c::AnyConditional) = Bool -widenconst((; val)::Const) = isa(val, Type) ? Type{val} : typeof(val) +widenconst((; val)::Const) = typeof(val) +function widenconst(c::ConstType) + if !isdefined(c, :Typeof) + c.Typeof = Type{c.val} + end + return c.Typeof +end widenconst(m::MaybeUndef) = widenconst(m.typ) widenconst(c::PartialTypeVar) = TypeVar widenconst(t::PartialStruct) = t.typ diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index a7989777317c3..022b63bead75e 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -399,8 +399,8 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb)) return Bool end # type-lattice for Const and PartialStruct wrappers - if ((isa(typea, PartialStruct) || isa(typea, Const)) && - (isa(typeb, PartialStruct) || isa(typeb, Const))) + if ((isa(typea, PartialStruct) || isConst(typea)) && + (isa(typeb, PartialStruct) || isConst(typeb))) aty = widenconst(typea) bty = widenconst(typeb) if aty === bty @@ -614,7 +614,7 @@ end # compute typeintersect over the extended inference lattice # where v is in the extended lattice, and t is a Type function tmeet(@nospecialize(v), @nospecialize(t)) - if isa(v, Const) + if isConst(v) if !has_free_typevars(t) && !isa(v.val, t) return Bottom end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index c6f98939e5e14..74ae5090329a6 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -28,9 +28,12 @@ end function has_nontrivial_const_info(@nospecialize t) isa(t, PartialStruct) && return true isa(t, PartialOpaque) && return true + if isa(t, ConstType) + return !hasuniquerep(t.val) + end isa(t, Const) || return false val = t.val - return !isdefined(typeof(val), :instance) && !(isa(val, Type) && hasuniquerep(val)) + return !isdefined(typeof(val), :instance) end has_const_info(@nospecialize x) = (!isa(x, Type) && !isvarargtype(x)) || isType(x) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 9b1106e964919..12f32b69b66b6 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -230,7 +230,7 @@ end ######### function singleton_type(@nospecialize(ft)) - if isa(ft, Const) + if isConst(ft) return ft.val elseif isconstType(ft) return ft.parameters[1] @@ -240,6 +240,19 @@ function singleton_type(@nospecialize(ft)) return nothing end +function maybe_singleton_const(@nospecialize(t)) + if isa(t, DataType) && isdefined(t, :instance) + if t === typeof(Bottom) + return ConstType(Bottom) + else + return Const(t.instance) + end + elseif isconstType(t) + return ConstType(t.parameters[1], t) + end + return t +end + ################### # SSAValues/Slots # ################### diff --git a/base/show.jl b/base/show.jl index 8359690034c23..4a805edf86f89 100644 --- a/base/show.jl +++ b/base/show.jl @@ -2489,6 +2489,7 @@ module IRShow import .Compiler: IRCode, ReturnNode, GotoIfNot, CFG, scan_ssa_use!, Argument, isexpr, compute_basic_blocks, block_for_inst, TriState, Effects, ALWAYS_TRUE, ALWAYS_FALSE + Base.:(==)(a::Compiler.ConstType, b::Compiler.ConstType) = Compiler.:(==)(a, b) Base.getindex(r::Compiler.StmtRange, ind::Integer) = Compiler.getindex(r, ind) Base.size(r::Compiler.StmtRange) = Compiler.size(r) Base.first(r::Compiler.StmtRange) = Compiler.first(r) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 10c204e1c50a6..83076d84125a7 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -983,7 +983,10 @@ '())) ;; otherwise do an assignment to trigger an error (= (outerref ,name) ,name))) - (= (outerref ,name) ,name)) + (block + (call (core set_binding_type!) (thismodule) (inert ,name) (call (core apply_type) (core Type) ,name)) + (= (outerref ,name) ,name))) + ;; Set the binding type to Type{...} to speed up inference (call (core _typebody!) ,name (call (core svec) ,@field-types)) (null))) ;; "inner" constructors diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 61058f9589f52..dd93d5422a7b3 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # tests for Core.Compiler correctness and precision -import Core.Compiler: Const, Conditional, ⊑, ReturnNode, GotoIfNot +import Core.Compiler: Const, ConstType, Conditional, ⊑, ReturnNode, GotoIfNot isdispatchelem(@nospecialize x) = !isa(x, Type) || Core.Compiler.isdispatchelem(x) using Random, Core.IR @@ -722,8 +722,8 @@ let fieldtype_tfunc = Core.Compiler.fieldtype_tfunc, @test fieldtype_tfunc(Union{Type{Base.RefValue{T}}, Type{Int32}} where {T<:Array}, Const(:x)) == Type{<:Array} @test fieldtype_tfunc(Union{Type{Base.RefValue{T}}, Type{Int32}} where {T<:Real}, Const(:x)) == Type{<:Real} @test fieldtype_tfunc(Union{Type{Base.RefValue{<:Array}}, Type{Int32}}, Const(:x)) == Type{Array} - @test fieldtype_tfunc(Union{Type{Base.RefValue{<:Real}}, Type{Int32}}, Const(:x)) == Const(Real) - @test fieldtype_tfunc(Const(Union{Base.RefValue{<:Real}, Type{Int32}}), Const(:x)) == Const(Real) + @test fieldtype_tfunc(Union{Type{Base.RefValue{<:Real}}, Type{Int32}}, Const(:x)) == ConstType(Real) + @test fieldtype_tfunc(ConstType(Union{Base.RefValue{<:Real}, Type{Int32}}), Const(:x)) == ConstType(Real) @test fieldtype_tfunc(Type{Union{Base.RefValue{T}, Type{Int32}}} where {T<:Real}, Const(:x)) == Type{<:Real} @test fieldtype_tfunc(Type{<:Tuple}, Const(1)) == Any @test fieldtype_tfunc(Type{<:Tuple}, Any) == Any @@ -731,11 +731,11 @@ let fieldtype_tfunc = Core.Compiler.fieldtype_tfunc, @test !fieldtype_nothrow(Type{Union{}}, Const(:x)) @test !fieldtype_nothrow(Union{Type{Base.RefValue{T}}, Int32} where {T<:Real}, Const(:x)) @test !fieldtype_nothrow(Union{Type{Base.RefValue{<:Real}}, Int32}, Const(:x)) - @test fieldtype_nothrow(Const(Union{Base.RefValue{<:Real}, Int32}), Const(:x)) + @test fieldtype_nothrow(ConstType(Union{Base.RefValue{<:Real}, Int32}), Const(:x)) @test !fieldtype_nothrow(Type{Union{Base.RefValue{T}, Int32}} where {T<:Real}, Const(:x)) # improvable? @test fieldtype_nothrow(Union{Type{Base.RefValue{T}}, Type{Base.RefValue{Any}}} where {T<:Real}, Const(:x)) @test fieldtype_nothrow(Union{Type{Base.RefValue{<:Real}}, Type{Base.RefValue{Any}}}, Const(:x)) - @test fieldtype_nothrow(Const(Union{Base.RefValue{<:Real}, Base.RefValue{Any}}), Const(:x)) + @test fieldtype_nothrow(ConstType(Union{Base.RefValue{<:Real}, Base.RefValue{Any}}), Const(:x)) @test fieldtype_nothrow(Type{Union{Base.RefValue{T}, Base.RefValue{Any}}} where {T<:Real}, Const(:x)) @test !fieldtype_nothrow(Type{Tuple{}}, Const(1)) @test fieldtype_nothrow(Type{Tuple{Int}}, Const(1)) @@ -1329,37 +1329,37 @@ isdefined_f3(x) = isdefined(x, 3) @test find_call(first(code_typed(isdefined_f3, Tuple{Tuple{Vararg{Int}}})[1]), isdefined, 3) let isa_tfunc = Core.Compiler.isa_tfunc - @test isa_tfunc(Array, Const(AbstractArray)) === Const(true) + @test isa_tfunc(Array, ConstType(AbstractArray)) === Const(true) @test isa_tfunc(Array, Type{AbstractArray}) === Const(true) @test isa_tfunc(Array, Type{AbstractArray{Int}}) == Bool @test isa_tfunc(Array{Real}, Type{AbstractArray{Int}}) === Const(false) - @test isa_tfunc(Array{Real, 2}, Const(AbstractArray{Real, 2})) === Const(true) - @test isa_tfunc(Array{Real, 2}, Const(AbstractArray{Int, 2})) === Const(false) + @test isa_tfunc(Array{Real, 2}, ConstType(AbstractArray{Real, 2})) === Const(true) + @test isa_tfunc(Array{Real, 2}, ConstType(AbstractArray{Int, 2})) === Const(false) @test isa_tfunc(DataType, Int) === Union{} - @test isa_tfunc(DataType, Const(Type{Int})) === Bool - @test isa_tfunc(DataType, Const(Type{Array})) === Bool - @test isa_tfunc(UnionAll, Const(Type{Int})) === Bool # could be improved - @test isa_tfunc(UnionAll, Const(Type{Array})) === Bool - @test isa_tfunc(Union, Const(Union{Float32, Float64})) === Bool + @test isa_tfunc(DataType, ConstType(Type{Int})) === Bool + @test isa_tfunc(DataType, ConstType(Type{Array})) === Bool + @test isa_tfunc(UnionAll, ConstType(Type{Int})) === Bool # could be improved + @test isa_tfunc(UnionAll, ConstType(Type{Array})) === Bool + @test isa_tfunc(Union, ConstType(Union{Float32, Float64})) === Bool @test isa_tfunc(Union, Type{Union}) === Const(true) - @test isa_tfunc(typeof(Union{}), Const(Int)) === Const(false) - @test isa_tfunc(typeof(Union{}), Const(Union{})) === Const(false) + @test isa_tfunc(typeof(Union{}), ConstType(Int)) === Const(false) + @test isa_tfunc(typeof(Union{}), ConstType(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), typeof(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), Union{}) === Union{} # any result is ok @test isa_tfunc(typeof(Union{}), Type{typeof(Union{})}) === Const(true) - @test isa_tfunc(typeof(Union{}), Const(typeof(Union{}))) === Const(true) - let c = Conditional(Core.SlotNumber(0), Const(Union{}), Const(Union{})) - @test isa_tfunc(c, Const(Bool)) === Const(true) + @test isa_tfunc(typeof(Union{}), ConstType(typeof(Union{}))) === Const(true) + let c = Conditional(Core.SlotNumber(0), ConstType(Union{}), ConstType(Union{})) + @test isa_tfunc(c, ConstType(Bool)) === Const(true) @test isa_tfunc(c, Type{Bool}) === Const(true) - @test isa_tfunc(c, Const(Real)) === Const(true) + @test isa_tfunc(c, ConstType(Real)) === Const(true) @test isa_tfunc(c, Type{Real}) === Const(true) - @test isa_tfunc(c, Const(Signed)) === Const(false) + @test isa_tfunc(c, ConstType(Signed)) === Const(false) @test isa_tfunc(c, Type{Complex}) === Const(false) @test isa_tfunc(c, Type{Complex{T}} where T) === Const(false) end @test isa_tfunc(Val{1}, Type{Val{T}} where T) === Bool @test isa_tfunc(Val{1}, DataType) === Bool - @test isa_tfunc(Any, Const(Any)) === Const(true) + @test isa_tfunc(Any, ConstType(Any)) === Const(true) @test isa_tfunc(Any, Union{}) === Union{} # any result is ok @test isa_tfunc(Any, Type{Union{}}) === Const(false) @test isa_tfunc(Union{Int64, Float64}, Type{Real}) === Const(true) @@ -1368,46 +1368,46 @@ let isa_tfunc = Core.Compiler.isa_tfunc end let subtype_tfunc = Core.Compiler.subtype_tfunc - @test subtype_tfunc(Type{<:Array}, Const(AbstractArray)) === Const(true) + @test subtype_tfunc(Type{<:Array}, ConstType(AbstractArray)) === Const(true) @test subtype_tfunc(Type{<:Array}, Type{AbstractArray}) === Const(true) @test subtype_tfunc(Type{<:Array}, Type{AbstractArray{Int}}) == Bool @test subtype_tfunc(Type{<:Array{Real}}, Type{AbstractArray{Int}}) === Const(false) - @test subtype_tfunc(Type{<:Array{Real, 2}}, Const(AbstractArray{Real, 2})) === Const(true) - @test subtype_tfunc(Type{Array{Real, 2}}, Const(AbstractArray{Int, 2})) === Const(false) + @test subtype_tfunc(Type{<:Array{Real, 2}}, ConstType(AbstractArray{Real, 2})) === Const(true) + @test subtype_tfunc(Type{Array{Real, 2}}, ConstType(AbstractArray{Int, 2})) === Const(false) @test subtype_tfunc(DataType, Int) === Bool - @test subtype_tfunc(DataType, Const(Type{Int})) === Bool - @test subtype_tfunc(DataType, Const(Type{Array})) === Bool - @test subtype_tfunc(UnionAll, Const(Type{Int})) === Bool - @test subtype_tfunc(UnionAll, Const(Type{Array})) === Bool - @test subtype_tfunc(Union, Const(Union{Float32, Float64})) === Bool + @test subtype_tfunc(DataType, ConstType(Type{Int})) === Bool + @test subtype_tfunc(DataType, ConstType(Type{Array})) === Bool + @test subtype_tfunc(UnionAll, ConstType(Type{Int})) === Bool + @test subtype_tfunc(UnionAll, ConstType(Type{Array})) === Bool + @test subtype_tfunc(Union, ConstType(Union{Float32, Float64})) === Bool @test subtype_tfunc(Union, Type{Union}) === Bool - @test subtype_tfunc(Union{}, Const(Int)) === Const(true) # any result is ok - @test subtype_tfunc(Union{}, Const(Union{})) === Const(true) # any result is ok + @test subtype_tfunc(Union{}, ConstType(Int)) === Const(true) # any result is ok + @test subtype_tfunc(Union{}, ConstType(Union{})) === Const(true) # any result is ok @test subtype_tfunc(Union{}, typeof(Union{})) === Const(true) # any result is ok @test subtype_tfunc(Union{}, Union{}) === Const(true) # any result is ok @test subtype_tfunc(Union{}, Type{typeof(Union{})}) === Const(true) # any result is ok - @test subtype_tfunc(Union{}, Const(typeof(Union{}))) === Const(true) # any result is ok - @test subtype_tfunc(typeof(Union{}), Const(typeof(Union{}))) === Const(true) # Union{} <: typeof(Union{}) - @test subtype_tfunc(typeof(Union{}), Const(Int)) === Const(true) # Union{} <: Int - @test subtype_tfunc(typeof(Union{}), Const(Union{})) === Const(true) # Union{} <: Union{} + @test subtype_tfunc(Union{}, ConstType(typeof(Union{}))) === Const(true) # any result is ok + @test subtype_tfunc(typeof(Union{}), ConstType(typeof(Union{}))) === Const(true) # Union{} <: typeof(Union{}) + @test subtype_tfunc(typeof(Union{}), ConstType(Int)) === Const(true) # Union{} <: Int + @test subtype_tfunc(typeof(Union{}), ConstType(Union{})) === Const(true) # Union{} <: Union{} @test subtype_tfunc(typeof(Union{}), Type{typeof(Union{})}) === Const(true) # Union{} <: Union{} @test subtype_tfunc(typeof(Union{}), Type{typeof(Union{})}) === Const(true) # Union{} <: typeof(Union{}) @test subtype_tfunc(typeof(Union{}), Type{Union{}}) === Const(true) # Union{} <: Union{} @test subtype_tfunc(Type{Union{}}, typeof(Union{})) === Const(true) # Union{} <: Union{} - @test subtype_tfunc(Type{Union{}}, Const(typeof(Union{}))) === Const(true) # Union{} <: typeof(Union{}) - @test subtype_tfunc(Type{Union{}}, Const(Int)) === Const(true) # Union{} <: typeof(Union{}) + @test subtype_tfunc(Type{Union{}}, ConstType(typeof(Union{}))) === Const(true) # Union{} <: typeof(Union{}) + @test subtype_tfunc(Type{Union{}}, ConstType(Int)) === Const(true) # Union{} <: typeof(Union{}) @test subtype_tfunc(Type{Union{}}, Any) === Const(true) # Union{} <: Any @test subtype_tfunc(Type{Union{}}, Union{Type{Int64}, Type{Float64}}) === Const(true) @test subtype_tfunc(Type{Union{}}, Union{Type{T}, Type{Float64}} where T) === Const(true) - let c = Conditional(Core.SlotNumber(0), Const(Union{}), Const(Union{})) - @test subtype_tfunc(c, Const(Bool)) === Const(true) # any result is ok + let c = Conditional(Core.SlotNumber(0), ConstType(Union{}), ConstType(Union{})) + @test subtype_tfunc(c, ConstType(Bool)) === Const(true) # any result is ok end @test subtype_tfunc(Type{Val{1}}, Type{Val{T}} where T) === Bool @test subtype_tfunc(Type{Val{1}}, DataType) === Bool @test subtype_tfunc(Type, Type{Val{T}} where T) === Bool @test subtype_tfunc(Type{Val{T}} where T, Type) === Bool - @test subtype_tfunc(Any, Const(Any)) === Const(true) - @test subtype_tfunc(Type{Any}, Const(Any)) === Const(true) + @test subtype_tfunc(Any, ConstType(Any)) === Const(true) + @test subtype_tfunc(Type{Any}, ConstType(Any)) === Const(true) @test subtype_tfunc(Any, Union{}) === Bool # any result is ok @test subtype_tfunc(Type{Any}, Union{}) === Const(false) # any result is ok @test subtype_tfunc(Type, Union{}) === Bool # any result is ok @@ -1424,7 +1424,7 @@ let egal_tfunc return r end @test egal_tfunc(Const(12345.12345), Const(12344.12345 + 1)) == Const(true) - @test egal_tfunc(Array, Const(Array)) === Const(false) + @test egal_tfunc(Array, ConstType(Array)) === Const(false) @test egal_tfunc(Array, Type{Array}) === Const(false) @test egal_tfunc(Int, Int) == Bool @test egal_tfunc(Array, Array) == Bool @@ -1433,27 +1433,27 @@ let egal_tfunc @test egal_tfunc(Array{Real, 2}, AbstractArray{Real, 2}) === Bool @test egal_tfunc(Array{Real, 2}, AbstractArray{Int, 2}) === Const(false) @test egal_tfunc(DataType, Int) === Const(false) - @test egal_tfunc(DataType, Const(Int)) === Bool - @test egal_tfunc(DataType, Const(Array)) === Const(false) - @test egal_tfunc(UnionAll, Const(Int)) === Const(false) - @test egal_tfunc(UnionAll, Const(Array)) === Bool - @test egal_tfunc(Union, Const(Union{Float32, Float64})) === Bool - @test egal_tfunc(Const(Union{Float32, Float64}), Const(Union{Float32, Float64})) === Const(true) + @test egal_tfunc(DataType, ConstType(Int)) === Bool + @test egal_tfunc(DataType, ConstType(Array)) === Const(false) + @test egal_tfunc(UnionAll, ConstType(Int)) === Const(false) + @test egal_tfunc(UnionAll, ConstType(Array)) === Bool + @test egal_tfunc(Union, ConstType(Union{Float32, Float64})) === Bool + @test egal_tfunc(ConstType(Union{Float32, Float64}), ConstType(Union{Float32, Float64})) === Const(true) @test egal_tfunc(Type{Union{Float32, Float64}}, Type{Union{Float32, Float64}}) === Bool @test egal_tfunc(typeof(Union{}), typeof(Union{})) === Bool # could be improved - @test egal_tfunc(Const(typeof(Union{})), Const(typeof(Union{}))) === Const(true) - let c = Conditional(Core.SlotNumber(0), Const(Union{}), Const(Union{})) - @test egal_tfunc(c, Const(Bool)) === Const(false) + @test egal_tfunc(ConstType(typeof(Union{})), ConstType(typeof(Union{}))) === Const(true) + let c = Conditional(Core.SlotNumber(0), ConstType(Union{}), ConstType(Union{})) + @test egal_tfunc(c, ConstType(Bool)) === Const(false) @test egal_tfunc(c, Type{Bool}) === Const(false) - @test egal_tfunc(c, Const(Real)) === Const(false) + @test egal_tfunc(c, ConstType(Real)) === Const(false) @test egal_tfunc(c, Type{Real}) === Const(false) - @test egal_tfunc(c, Const(Signed)) === Const(false) + @test egal_tfunc(c, ConstType(Signed)) === Const(false) @test egal_tfunc(c, Type{Complex}) === Const(false) @test egal_tfunc(c, Type{Complex{T}} where T) === Const(false) @test egal_tfunc(c, Bool) === Bool @test egal_tfunc(c, Any) === Bool end - let c = Conditional(Core.SlotNumber(0), Union{}, Const(Union{})) # === Const(false) + let c = Conditional(Core.SlotNumber(0), Union{}, ConstType(Union{})) # === Const(false) @test egal_tfunc(c, Const(false)) === Conditional(c.var, c.elsetype, Union{}) @test egal_tfunc(c, Const(true)) === Conditional(c.var, Union{}, c.elsetype) @test egal_tfunc(c, Const(nothing)) === Const(false) @@ -1461,7 +1461,7 @@ let egal_tfunc @test egal_tfunc(c, Bool) === Bool @test egal_tfunc(c, Any) === Bool end - let c = Conditional(Core.SlotNumber(0), Const(Union{}), Union{}) # === Const(true) + let c = Conditional(Core.SlotNumber(0), ConstType(Union{}), Union{}) # === Const(true) @test egal_tfunc(c, Const(false)) === Conditional(c.var, Union{}, c.vtype) @test egal_tfunc(c, Const(true)) === Conditional(c.var, c.vtype, Union{}) @test egal_tfunc(c, Const(nothing)) === Const(false) @@ -1471,7 +1471,7 @@ let egal_tfunc end @test egal_tfunc(Type{Val{1}}, Type{Val{T}} where T) === Bool @test egal_tfunc(Type{Val{1}}, DataType) === Bool - @test egal_tfunc(Const(Any), Const(Any)) === Const(true) + @test egal_tfunc(ConstType(Any), ConstType(Any)) === Const(true) @test egal_tfunc(Any, Union{}) === Const(false) # any result is ok @test egal_tfunc(Type{Any}, Type{Union{}}) === Const(false) @test egal_tfunc(Union{Int64, Float64}, Real) === Bool @@ -1489,13 +1489,13 @@ egal_conditional_lattice3(x, y) = x === y + y ? "" : 1 @test Base.return_types(egal_conditional_lattice3, (Int32, Int64)) == Any[Int] using Core.Compiler: PartialStruct, nfields_tfunc, sizeof_tfunc, sizeof_nothrow -@test sizeof_tfunc(Const(Ptr)) === sizeof_tfunc(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}}) === Const(Sys.WORD_SIZE ÷ 8) +@test sizeof_tfunc(ConstType(Ptr)) === sizeof_tfunc(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}}) === Const(Sys.WORD_SIZE ÷ 8) @test sizeof_tfunc(Type{Ptr}) === Const(sizeof(Ptr)) @test sizeof_nothrow(Union{Ptr, Int, Type{Ptr{Int8}}, Type{Int}}) -@test sizeof_nothrow(Const(Ptr)) +@test sizeof_nothrow(ConstType(Ptr)) @test sizeof_nothrow(Type{Ptr}) @test sizeof_nothrow(Type{Union{Ptr{Int}, Int}}) -@test !sizeof_nothrow(Const(Tuple)) +@test !sizeof_nothrow(ConstType(Tuple)) @test !sizeof_nothrow(Type{Vector{Int}}) @test !sizeof_nothrow(Type{Union{Int, String}}) @test sizeof_nothrow(String) @@ -3012,9 +3012,9 @@ const DenseIdx = Union{IntRange,Integer} # Non uniformity in expressions with PartialTypeVar @test Core.Compiler.:⊑(Core.Compiler.PartialTypeVar(TypeVar(:N), true, true), TypeVar) let N = TypeVar(:N) - @test Core.Compiler.apply_type_nothrow([Core.Compiler.Const(NTuple), + @test Core.Compiler.apply_type_nothrow([Core.Compiler.ConstType(NTuple), Core.Compiler.PartialTypeVar(N, true, true), - Core.Compiler.Const(Any)], Type{Tuple{Vararg{Any,N}}}) + Core.Compiler.ConstType(Any)], Type{Tuple{Vararg{Any,N}}}) end # issue #33768