Skip to content

Commit 6540021

Browse files
committed
fix #20704, pure annotation should not skip method errors
1 parent 474d05d commit 6540021

File tree

2 files changed

+61
-35
lines changed

2 files changed

+61
-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
@@ -1544,7 +1546,7 @@ function return_type_tfunc(argtypes::ANY, vtypes::VarTable, sv::InferenceState)
15441546
return NF
15451547
end
15461548

1547-
function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, vtypes::VarTable, sv::InferenceState)
1549+
function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, sv::InferenceState)
15481550
for i = 2:length(argtypes)
15491551
a = argtypes[i]
15501552
if !(isa(a,Const) || isconstType(a))
@@ -1569,7 +1571,7 @@ function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, vtypes::VarTable, sv:
15691571
try
15701572
value = Core._apply_pure(f, args)
15711573
# TODO: add some sort of edge(s)
1572-
return abstract_eval_constant(value)
1574+
return Const(value,true)
15731575
catch
15741576
return false
15751577
end
@@ -1804,7 +1806,7 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect
18041806
end
18051807

18061808
atype = argtypes_to_type(argtypes)
1807-
t = pure_eval_call(f, argtypes, atype, vtypes, sv)
1809+
t = pure_eval_call(f, argtypes, atype, sv)
18081810
t !== false && return t
18091811

18101812
if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :typejoin)
@@ -1976,14 +1978,7 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState)
19761978
return t
19771979
end
19781980

1979-
const Type_Array = Const(Array)
1980-
1981-
function abstract_eval_constant(x::ANY)
1982-
if x === Array
1983-
return Type_Array
1984-
end
1985-
return Const(x)
1986-
end
1981+
const abstract_eval_constant = Const
19871982

19881983
function abstract_eval_global(M::Module, s::Symbol)
19891984
if isdefined(M,s) && isconst(M,s)
@@ -2927,28 +2922,33 @@ function optimize(me::InferenceState)
29272922

29282923
if isa(me.bestguess, Const) || isconstType(me.bestguess)
29292924
me.const_ret = true
2930-
ispure = me.src.pure
2931-
if !ispure && length(me.src.code) < 10
2932-
ispure = true
2925+
proven_pure = false
2926+
# must be proven pure to use const_api; otherwise we might skip throwing errors
2927+
# (issue #20704)
2928+
# TODO: Improve this analysis; if a function is marked @pure we should really
2929+
# only care about certain errors (e.g. method errors and type errors).
2930+
if length(me.src.code) < 10
2931+
proven_pure = true
29332932
for stmt in me.src.code
29342933
if !statement_effect_free(stmt, me.src, me.mod)
2935-
ispure = false
2934+
proven_pure = false
29362935
break
29372936
end
29382937
end
2939-
if ispure
2938+
if proven_pure
29402939
for fl in me.src.slotflags
29412940
if (fl & Slot_UsedUndef) != 0
2942-
ispure = false
2941+
proven_pure = false
29432942
break
29442943
end
29452944
end
29462945
end
29472946
end
2948-
me.src.pure = ispure
2947+
if proven_pure
2948+
me.src.pure = true
2949+
end
29492950

2950-
do_coverage = coverage_enabled()
2951-
if ispure && !do_coverage
2951+
if proven_pure && !coverage_enabled()
29522952
# use constant calling convention
29532953
# Do not emit `jlcall_api == 2` if coverage is enabled
29542954
# so that we don't need to add coverage support
@@ -3079,7 +3079,7 @@ function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, unde
30793079
undefs[id] = true
30803080
end
30813081
# add type annotations where needed
3082-
if !(sv.src.slottypes[id] <: vt)
3082+
if !(sv.src.slottypes[id] vt)
30833083
e.args[i] = TypedSlot(id, vt)
30843084
end
30853085
end
@@ -3222,6 +3222,7 @@ end
32223222
# we also need to preserve the type for any untyped load of a DataType
32233223
# since codegen optimizations of functions like `is` will depend on knowing it
32243224
function widen_slot_type(ty::ANY, untypedload::Bool)
3225+
ty = widenconst(ty)
32253226
if isa(ty, DataType)
32263227
if untypedload || isbits(ty) || isdefined(ty, :instance)
32273228
return ty
@@ -3767,23 +3768,26 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
37673768
atype_unlimited, method.sig)
37683769
methsp = methsp::SimpleVector
37693770
end
3770-
# check whether call can be inlined to just a quoted constant value
3771-
if isa(f, widenconst(ft)) && !method.isstaged && (method.source.pure || f === return_type)
3772-
if isconstType(e.typ)
3773-
return inline_as_constant(e.typ.parameters[1], argexprs, sv,
3774-
invoke_data)
3775-
elseif isa(e.typ,Const)
3776-
return inline_as_constant(e.typ.val, argexprs, sv,
3777-
invoke_data)
3778-
end
3779-
end
37803771

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

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

test/inference.jl

+22
Original file line numberDiff line numberDiff line change
@@ -648,3 +648,25 @@ let A = 1:2, z = zip(A, A, A, A, A, A, A, A, A, A, A, A)
648648
@test z isa Core.Inference.limit_type_depth(typeof(z), 0)
649649
@test start(z) == (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, 1)))))))))))
650650
end
651+
652+
# issue #20704
653+
f20704(::Int) = 1
654+
Base.@pure b20704(x::ANY) = f20704(x)
655+
@test b20704(42) === 1
656+
@test_throws MethodError b20704(42.0)
657+
658+
bb20704() = b20704(Any[1.0][1])
659+
@test_throws MethodError bb20704()
660+
661+
v20704() = Val{b20704(Any[1.0][1])}
662+
@test_throws MethodError v20704()
663+
@test Base.return_types(v20704, ()) == Any[Type{Val{1}}]
664+
665+
Base.@pure g20704(::Int) = 1
666+
h20704(x::ANY) = g20704(x)
667+
@test g20704(1) === 1
668+
@test_throws MethodError h20704(1.2)
669+
670+
Base.@pure c20704() = (f20704(1.0); 1)
671+
d20704() = c20704()
672+
@test_throws MethodError d20704()

0 commit comments

Comments
 (0)