@@ -221,34 +221,53 @@ function InferenceState(result::InferenceResult, cached::Symbol, interp::GPUInte
221
221
src === nothing && return nothing
222
222
validate_code_in_debug_mode (result. linfo, src, " lowered" )
223
223
validate_globalrefs (interp, result. linfo, src)
224
+ validate_code_in_debug_mode (result. linfo, src, " validated" )
225
+ errors = Core. Compiler. validate_code (result. linfo, src)
226
+ @safe_info " errors" errors src. code
224
227
return InferenceState (result, src, cached, interp)
225
228
end
226
229
230
+ const UNDEFINED_GLOBAL = " use of an undefined global binding"
231
+ const MUTABLE_GLOBAL = " use of a mutable global binding"
232
+
227
233
function validate_globalrefs (interp, mi, src)
228
- function validate (x)
234
+ # pseudo (single-frame) backtrace pointing to a source code location
235
+ function backtrace (i)
236
+ loc = src. linetable[i]
237
+ [StackTraces. StackFrame (loc. method, loc. file, loc. line, mi, false , false , C_NULL )]
238
+ end
239
+
240
+ function validate (i, x, errors:: Vector{IRError} )
229
241
if x isa Expr
230
- return Expr (x. head, validate .(x. args))
242
+ for y in x. args
243
+ validate (i, y, errors)
244
+ end
231
245
elseif x isa GlobalRef
232
246
Base. isbindingresolved (x. mod, x. name) || return
233
247
# XXX : when does this happen? do we miss any cases by bailing out early?
234
248
# why doesn't calling `Base.resolve(x, force=true)` work?
235
249
if ! Base. isdefined (x. mod, x. name)
236
- throw ( KernelError (interp . job, " using undefined global: $(x . mod) . $(x . name) " ))
250
+ push! (errors, (UNDEFINED_GLOBAL, backtrace (i), x ))
237
251
end
238
252
if ! Base. isconst (x. mod, x. name)
239
- throw ( KernelError (interp . job, " using mutable global: $(x . mod) . $(x . name) " ))
253
+ push! (errors, (MUTABLE_GLOBAL, backtrace (i), x ))
240
254
end
241
- # XXX : can we use KernelError? and make the validation conditional? both are
242
- # complicated by the fact that we don't have the CompilerJob here,
243
- # and that inference results can be cached across jobs.
255
+
256
+ # TODO : make the validation conditional, but make sure we don't cache invalid IR
244
257
245
258
# TODO : perform more validation? e.g. disallow Arrays and other CPU values?
246
- # probably requires an interface, so again access to the CompilerJob
247
- # (as a CPU-back-end would still support such values).
248
259
end
260
+
261
+ return
249
262
end
250
263
251
- validate .(src. code)
264
+ errors = IRError[]
265
+ for (i, x) in enumerate (src. code)
266
+ validate (i, x, errors)
267
+ end
268
+ if ! isempty (errors)
269
+ throw (InvalidIRError (interp. job, errors))
270
+ end
252
271
253
272
return
254
273
end
0 commit comments