Skip to content

Commit 8fee77f

Browse files
committed
fix #20704, pure annotation should not skip method errors
1 parent 5c61a0a commit 8fee77f

File tree

2 files changed

+68
-35
lines changed

2 files changed

+68
-35
lines changed

base/inference.jl

+39-35
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ end
6464
# The type of a value might be constant
6565
struct Const
6666
val
67-
Const(v::ANY) = new(v)
67+
actual::Bool # if true, we obtained `val` by actually calling a @pure function
68+
Const(v::ANY) = new(v, false)
69+
Const(v::ANY, a::Bool) = new(v, a)
6870
end
6971

7072
# The type of a value might be Bool,
@@ -191,7 +193,7 @@ mutable struct InferenceState
191193
vararg_type = rewrap(vararg_type, linfo.specTypes)
192194
end
193195
s_types[1][la] = VarState(vararg_type, false)
194-
src.slottypes[la] = widenconst(vararg_type)
196+
src.slottypes[la] = vararg_type
195197
la -= 1
196198
end
197199
end
@@ -220,11 +222,11 @@ mutable struct InferenceState
220222
end
221223
i == laty && (lastatype = atyp)
222224
s_types[1][i] = VarState(atyp, false)
223-
src.slottypes[i] = widenconst(atyp)
225+
src.slottypes[i] = atyp
224226
end
225227
for i = (atail + 1):la
226228
s_types[1][i] = VarState(lastatype, false)
227-
src.slottypes[i] = widenconst(lastatype)
229+
src.slottypes[i] = lastatype
228230
end
229231
else
230232
@assert la == 0 # wrong number of arguments
@@ -1546,7 +1548,7 @@ function return_type_tfunc(argtypes::ANY, vtypes::VarTable, sv::InferenceState)
15461548
return NF
15471549
end
15481550

1549-
function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, vtypes::VarTable, sv::InferenceState)
1551+
function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, sv::InferenceState)
15501552
for i = 2:length(argtypes)
15511553
a = argtypes[i]
15521554
if !(isa(a,Const) || isconstType(a))
@@ -1571,7 +1573,7 @@ function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, vtypes::VarTable, sv:
15711573
try
15721574
value = Core._apply_pure(f, args)
15731575
# TODO: add some sort of edge(s)
1574-
return abstract_eval_constant(value)
1576+
return Const(value, true)
15751577
catch
15761578
return false
15771579
end
@@ -1807,7 +1809,7 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect
18071809
end
18081810

18091811
atype = argtypes_to_type(argtypes)
1810-
t = pure_eval_call(f, argtypes, atype, vtypes, sv)
1812+
t = pure_eval_call(f, argtypes, atype, sv)
18111813
t !== false && return t
18121814

18131815
if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :typejoin)
@@ -1979,14 +1981,7 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState)
19791981
return t
19801982
end
19811983

1982-
const Type_Array = Const(Array)
1983-
1984-
function abstract_eval_constant(x::ANY)
1985-
if x === Array
1986-
return Type_Array
1987-
end
1988-
return Const(x)
1989-
end
1984+
const abstract_eval_constant = Const
19901985

19911986
function abstract_eval_global(M::Module, s::Symbol)
19921987
if isdefined(M,s) && isconst(M,s)
@@ -2930,28 +2925,33 @@ function optimize(me::InferenceState)
29302925

29312926
if isa(me.bestguess, Const) || isconstType(me.bestguess)
29322927
me.const_ret = true
2933-
ispure = me.src.pure
2934-
if !ispure && length(me.src.code) < 10
2935-
ispure = true
2928+
proven_pure = false
2929+
# must be proven pure to use const_api; otherwise we might skip throwing errors
2930+
# (issue #20704)
2931+
# TODO: Improve this analysis; if a function is marked @pure we should really
2932+
# only care about certain errors (e.g. method errors and type errors).
2933+
if length(me.src.code) < 10
2934+
proven_pure = true
29362935
for stmt in me.src.code
29372936
if !statement_effect_free(stmt, me.src, me.mod)
2938-
ispure = false
2937+
proven_pure = false
29392938
break
29402939
end
29412940
end
2942-
if ispure
2941+
if proven_pure
29432942
for fl in me.src.slotflags
29442943
if (fl & Slot_UsedUndef) != 0
2945-
ispure = false
2944+
proven_pure = false
29462945
break
29472946
end
29482947
end
29492948
end
29502949
end
2951-
me.src.pure = ispure
2950+
if proven_pure
2951+
me.src.pure = true
2952+
end
29522953

2953-
do_coverage = coverage_enabled()
2954-
if ispure && !do_coverage
2954+
if proven_pure && !coverage_enabled()
29552955
# use constant calling convention
29562956
# Do not emit `jlcall_api == 2` if coverage is enabled
29572957
# so that we don't need to add coverage support
@@ -3082,7 +3082,7 @@ function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, unde
30823082
undefs[id] = true
30833083
end
30843084
# add type annotations where needed
3085-
if !(sv.src.slottypes[id] <: vt)
3085+
if !(sv.src.slottypes[id] vt)
30863086
e.args[i] = TypedSlot(id, vt)
30873087
end
30883088
end
@@ -3225,6 +3225,7 @@ end
32253225
# we also need to preserve the type for any untyped load of a DataType
32263226
# since codegen optimizations of functions like `is` will depend on knowing it
32273227
function widen_slot_type(ty::ANY, untypedload::Bool)
3228+
ty = widenconst(ty)
32283229
if isa(ty, DataType)
32293230
if untypedload || isbits(ty) || isdefined(ty, :instance)
32303231
return ty
@@ -3770,23 +3771,26 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
37703771
atype_unlimited, method.sig)
37713772
methsp = methsp::SimpleVector
37723773
end
3773-
# check whether call can be inlined to just a quoted constant value
3774-
if isa(f, widenconst(ft)) && !method.isstaged && (method.source.pure || f === return_type)
3775-
if isconstType(e.typ)
3776-
return inline_as_constant(e.typ.parameters[1], argexprs, sv,
3777-
invoke_data)
3778-
elseif isa(e.typ,Const)
3779-
return inline_as_constant(e.typ.val, argexprs, sv,
3780-
invoke_data)
3781-
end
3782-
end
37833774

37843775
methsig = method.sig
37853776
if !(atype <: metharg)
37863777
return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited,
37873778
invoke_data)
37883779
end
37893780

3781+
# check whether call can be inlined to just a quoted constant value
3782+
if isa(f, widenconst(ft)) && !method.isstaged
3783+
if f === return_type
3784+
if isconstType(e.typ)
3785+
return inline_as_constant(e.typ.parameters[1], argexprs, sv, invoke_data)
3786+
elseif isa(e.typ,Const)
3787+
return inline_as_constant(e.typ.val, argexprs, sv, invoke_data)
3788+
end
3789+
elseif method.source.pure && isa(e.typ,Const) && e.typ.actual
3790+
return inline_as_constant(e.typ.val, argexprs, sv, invoke_data)
3791+
end
3792+
end
3793+
37903794
argexprs0 = argexprs
37913795
na = Int(method.nargs)
37923796
# check for vararg function

test/inference.jl

+29
Original file line numberDiff line numberDiff line change
@@ -655,3 +655,32 @@ let A = 1:2, z = zip(A, A, A, A, A, A, A, A, A, A, A, A)
655655
@test z isa Core.Inference.limit_type_depth(typeof(z), 0)
656656
@test start(z) == (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, 1)))))))))))
657657
end
658+
659+
# issue #20704
660+
f20704(::Int) = 1
661+
Base.@pure b20704(x::ANY) = f20704(x)
662+
@test b20704(42) === 1
663+
@test_throws MethodError b20704(42.0)
664+
665+
bb20704() = b20704(Any[1.0][1])
666+
@test_throws MethodError bb20704()
667+
668+
v20704() = Val{b20704(Any[1.0][1])}
669+
@test_throws MethodError v20704()
670+
@test Base.return_types(v20704, ()) == Any[Type{Val{1}}]
671+
672+
Base.@pure g20704(::Int) = 1
673+
h20704(x::ANY) = g20704(x)
674+
@test g20704(1) === 1
675+
@test_throws MethodError h20704(1.2)
676+
677+
Base.@pure c20704() = (f20704(1.0); 1)
678+
d20704() = c20704()
679+
@test_throws MethodError d20704()
680+
681+
Base.@pure function a20704(x)
682+
rand()
683+
42
684+
end
685+
aa20704(x) = x(nothing)
686+
@test code_typed(aa20704, (typeof(a20704),))[1][1].pure

0 commit comments

Comments
 (0)