Skip to content

Commit 515e42b

Browse files
committed
LSAnalyzer: report no method error
This commit is certainly quite useful as it does find new errors, but it's still difficult to identify where the errors are coming from, and it's still noisy in the current state. In particular, it even reports type instabilities in the code generated by `@testset`, so I'm not sure what to do about it.
1 parent 2af5c0a commit 515e42b

File tree

1 file changed

+98
-1
lines changed

1 file changed

+98
-1
lines changed

src/analysis/Analyzer.jl

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,28 @@ non-concrete call sites in a toplevel frame created by `JET.virtual_process`.
151151
"""
152152
CC.bail_out_toplevel_call(::LSAnalyzer, ::CC.InferenceState) = false
153153

154+
function CC.abstract_call_gf_by_type(analyzer::LSAnalyzer,
155+
@nospecialize(func), arginfo::CC.ArgInfo, si::CC.StmtInfo, @nospecialize(atype), sv::CC.InferenceState,
156+
max_methods::Int)
157+
ret = @invoke CC.abstract_call_gf_by_type(analyzer::ToplevelAbstractAnalyzer,
158+
func::Any, arginfo::CC.ArgInfo, si::CC.StmtInfo, atype::Any, sv::CC.InferenceState, max_methods::Int)
159+
if !should_analyze(analyzer, sv)
160+
return ret
161+
end
162+
atype′ = Ref{Any}(atype)
163+
function after_abstract_call_gf_by_type(analyzer′::LSAnalyzer, sv′::CC.InferenceState)
164+
ret′ = ret[]
165+
report_method_error!(analyzer′, sv′, ret′, arginfo, atype′[])
166+
return true
167+
end
168+
if isready(ret)
169+
after_abstract_call_gf_by_type(analyzer, sv)
170+
else
171+
push!(sv.tasks, after_abstract_call_gf_by_type)
172+
end
173+
return ret
174+
end
175+
154176
# TODO Better to factor out and share it with `JET.JETAnalyzer`
155177
function CC.abstract_eval_globalref(analyzer::LSAnalyzer,
156178
g::GlobalRef, saw_latestworld::Bool, sv::CC.InferenceState)
@@ -185,6 +207,81 @@ end
185207
# analysis
186208
# ========
187209

210+
# MethodErrorReport
211+
# -----------------
212+
213+
@jetreport struct MethodErrorReport <: InferenceErrorReport
214+
@nospecialize t # ::Union{Type, Vector{Type}}
215+
union_split::Int
216+
end
217+
function JETInterface.print_report_message(io::IO, report::MethodErrorReport)
218+
print(io, "no matching method found ")
219+
if report.union_split == 0
220+
print_callsig(io, report.t)
221+
else
222+
ts = report.t::Vector{Any}
223+
nts = length(ts)
224+
for i = 1:nts
225+
print_callsig(io, ts[i])
226+
i == nts || print(io, ", ")
227+
end
228+
print(io, " (", nts, '/', report.union_split, " union split)")
229+
end
230+
end
231+
function print_callsig(io, @nospecialize(t))
232+
print(io, '`')
233+
Base.show_tuple_as_call(io, Symbol(""), t)
234+
print(io, '`')
235+
end
236+
inference_error_report_stack_impl(r::MethodErrorReport) = length(r.vst):-1:1
237+
inference_error_report_severity_impl(r::MethodErrorReport) = DiagnosticSeverity.Warning
238+
239+
function report_method_error!(analyzer::LSAnalyzer,
240+
sv::CC.InferenceState, call::CC.CallMeta, arginfo::CC.ArgInfo, @nospecialize(atype))
241+
info = call.info
242+
if isa(info, CC.ConstCallInfo)
243+
info = info.call
244+
end
245+
if isa(info, CC.MethodMatchInfo)
246+
report_method_error!(analyzer, sv, info, atype)
247+
elseif isa(info, CC.UnionSplitInfo)
248+
report_method_error_for_union_split!(analyzer, sv, info, arginfo)
249+
end
250+
end
251+
252+
function report_method_error!(analyzer::LSAnalyzer, sv::CC.InferenceState, info::CC.MethodMatchInfo, @nospecialize(atype))
253+
if CC.isempty(info.results)
254+
report = MethodErrorReport(sv, atype, 0)
255+
add_new_report!(analyzer, sv.result, report)
256+
return true
257+
end
258+
return false
259+
end
260+
261+
function report_method_error_for_union_split!(analyzer::LSAnalyzer, sv::CC.InferenceState, info::CC.UnionSplitInfo, arginfo::CC.ArgInfo)
262+
# check each match for union-split signature
263+
split_argtypes = empty_matches = nothing
264+
reported = false
265+
for (i, matchinfo) in enumerate(info.split)
266+
if CC.isempty(matchinfo.results)
267+
if isnothing(split_argtypes)
268+
split_argtypes = CC.switchtupleunion(CC.typeinf_lattice(analyzer), arginfo.argtypes)
269+
end
270+
argtypes′ = split_argtypes[i]::Vector{Any}
271+
if empty_matches === nothing
272+
empty_matches = (Any[], length(info.split))
273+
end
274+
sig_n = CC.argtypes_to_type(argtypes′)
275+
push!(empty_matches[1], sig_n)
276+
end
277+
end
278+
if empty_matches !== nothing
279+
add_new_report!(analyzer, sv.result, MethodErrorReport(sv, empty_matches...))
280+
reported |= true
281+
end
282+
return reported
283+
end
284+
188285
# UndefVarErrorReport
189286
# -------------------
190287

@@ -278,7 +375,7 @@ function LSAnalyzer(entry::AnalysisEntry,
278375
# Enable the `assume_bindings_static` option to terminate analysis a bit earlier when
279376
# there are undefined bindings detected. Note that this option will cause inference
280377
# cache inconsistency until JuliaLang/julia#40399 is merged. But the analysis cache of
281-
# JETAnalyzer has the same problem already anyway, so enabling this option does not
378+
# LSAnalyzer has the same problem already anyway, so enabling this option does not
282379
# make the situation worse.
283380
jetconfigs[:assume_bindings_static] = true
284381
state = AnalyzerState(world; jetconfigs...)

0 commit comments

Comments
 (0)