@@ -24,14 +24,16 @@ const VALID_EXPR_HEADS = ObjectIdDict(
24
24
:isdefined => 1 : 1 ,
25
25
:simdloop => 0 : 0 ,
26
26
:gc_preserve_begin => 0 : typemax (Int),
27
- :gc_preserve_end => 0 : typemax (Int)
27
+ :gc_preserve_end => 0 : typemax (Int),
28
+ :thunk => 1 : 1
28
29
)
29
30
30
31
# @enum isn't defined yet, otherwise I'd use it for this
31
32
const INVALID_EXPR_HEAD = " invalid expression head"
32
33
const INVALID_EXPR_NARGS = " invalid number of expression args"
33
34
const INVALID_LVALUE = " invalid LHS value"
34
35
const INVALID_RVALUE = " invalid RHS value"
36
+ const INVALID_RETURN = " invalid argument to :return"
35
37
const INVALID_CALL_ARG = " invalid :call argument"
36
38
const EMPTY_SLOTNAMES = " slotnames field is empty"
37
39
const SLOTFLAGS_MISMATCH = " length(slotnames) != length(slotflags)"
@@ -40,6 +42,7 @@ const SLOTTYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo slottypes field is no
40
42
const SSAVALUETYPES_MISMATCH = " not all SSAValues in AST have a type in ssavaluetypes"
41
43
const SSAVALUETYPES_MISMATCH_UNINFERRED = " uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues"
42
44
const NON_TOP_LEVEL_METHOD = " encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)"
45
+ const NON_TOP_LEVEL_GLOBAL = " encountered `Expr` head `:global` in non-top-level code (i.e. `nargs` > 0)"
43
46
const SIGNATURE_NARGS_MISMATCH = " method signature does not match number of method arguments"
44
47
const SLOTNAMES_NARGS_MISMATCH = " CodeInfo for method contains fewer slotnames than the number of method arguments"
45
48
@@ -56,44 +59,89 @@ InvalidCodeError(kind::AbstractString) = InvalidCodeError(kind, nothing)
56
59
Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`.
57
60
"""
58
61
function validate_code! (errors:: Vector{>:InvalidCodeError} , c:: CodeInfo , is_top_level:: Bool = false )
62
+ function validate_val! (@nospecialize (x))
63
+ if isa (x, Expr)
64
+ if x. head === :call || x. head === :invoke
65
+ f = x. args[1 ]
66
+ if f isa GlobalRef && (f. name === :llvmcall || f. name === :cglobal ) && x. head === :call
67
+ # TODO : these are not yet linearized
68
+ else
69
+ for arg in x. args
70
+ if ! is_valid_argument (arg)
71
+ push! (errors, InvalidCodeError (INVALID_CALL_ARG, arg))
72
+ else
73
+ validate_val! (arg)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ elseif isa (x, SSAValue)
79
+ id = x. id + 1 # ensures that id > 0 for use with BitSet
80
+ ! in (id, ssavals) && push! (ssavals, id)
81
+ end
82
+ end
83
+
59
84
ssavals = BitSet ()
60
85
lhs_slotnums = BitSet ()
61
- walkast ( c. code) do x
86
+ for x in c. code
62
87
if isa (x, Expr)
63
- ! is_top_level && x. head == :method && push! (errors, InvalidCodeError (NON_TOP_LEVEL_METHOD))
64
- narg_bounds = get (VALID_EXPR_HEADS, x. head, - 1 : - 1 )
88
+ head = x. head
89
+ if ! is_top_level
90
+ head === :method && push! (errors, InvalidCodeError (NON_TOP_LEVEL_METHOD))
91
+ head === :global && push! (errors, InvalidCodeError (NON_TOP_LEVEL_GLOBAL))
92
+ end
93
+ narg_bounds = get (VALID_EXPR_HEADS, head, - 1 : - 1 )
65
94
nargs = length (x. args)
66
95
if narg_bounds == - 1 : - 1
67
- push! (errors, InvalidCodeError (INVALID_EXPR_HEAD, (x . head, x)))
96
+ push! (errors, InvalidCodeError (INVALID_EXPR_HEAD, (head, x)))
68
97
elseif ! in (nargs, narg_bounds)
69
- push! (errors, InvalidCodeError (INVALID_EXPR_NARGS, (x . head, nargs, x)))
70
- elseif x . head == :(= )
98
+ push! (errors, InvalidCodeError (INVALID_EXPR_NARGS, (head, nargs, x)))
99
+ elseif head = == :(= )
71
100
lhs, rhs = x. args
72
101
if ! is_valid_lvalue (lhs)
73
102
push! (errors, InvalidCodeError (INVALID_LVALUE, lhs))
74
103
elseif isa (lhs, SlotNumber) && ! in (lhs. id, lhs_slotnums)
75
104
n = lhs. id
76
105
push! (lhs_slotnums, n)
77
106
end
78
- if ! is_valid_rvalue (rhs)
107
+ if ! is_valid_rvalue (lhs, rhs)
79
108
push! (errors, InvalidCodeError (INVALID_RVALUE, rhs))
80
109
end
81
- elseif x. head == :call || x. head == :invoke
82
- for arg in x. args
83
- if ! is_valid_rvalue (arg)
84
- push! (errors, InvalidCodeError (INVALID_CALL_ARG, arg))
85
- end
110
+ validate_val! (lhs)
111
+ validate_val! (rhs)
112
+ elseif head === :gotoifnot
113
+ if ! is_valid_argument (x. args[1 ])
114
+ push! (errors, InvalidCodeError (INVALID_CALL_ARG, x. args[1 ]))
115
+ end
116
+ validate_val! (x. args[1 ])
117
+ elseif head === :return
118
+ if ! is_valid_return (x. args[1 ])
119
+ push! (errors, InvalidCodeError (INVALID_RETURN, x. args[1 ]))
86
120
end
121
+ validate_val! (x. args[1 ])
122
+ elseif head === :call || head === :invoke || head == :gc_preserve_end || head === :meta ||
123
+ head === :inbounds || head === :foreigncall || head === :const || head === :enter ||
124
+ head === :leave || head === :method || head === :global || head === :static_parameter ||
125
+ head === :new || head === :thunk || head === :simdloop
126
+ validate_val! (x)
127
+ else
128
+ push! (errors, InvalidCodeError (" invalid statement" , x))
87
129
end
88
- elseif isa (x, SSAValue)
89
- id = x. id + 1 # ensures that id > 0 for use with BitSet
90
- ! in (id, ssavals) && push! (ssavals, id)
130
+ elseif isa (x, NewvarNode)
131
+ elseif isa (x, LabelNode)
132
+ elseif isa (x, GotoNode)
133
+ elseif x === nothing
134
+ elseif isa (x, SlotNumber)
135
+ elseif isa (x, GlobalRef)
136
+ elseif isa (x, LineNumberNode)
137
+ else
138
+ push! (errors, InvalidCodeError (" invalid statement" , x))
91
139
end
92
140
end
93
141
nslotnames = length (c. slotnames)
94
142
nslotflags = length (c. slotflags)
95
143
nssavals = length (ssavals)
96
- nslotnames == 0 && push! (errors, InvalidCodeError (EMPTY_SLOTNAMES))
144
+ ! is_top_level && nslotnames == 0 && push! (errors, InvalidCodeError (EMPTY_SLOTNAMES))
97
145
nslotnames != nslotflags && push! (errors, InvalidCodeError (SLOTFLAGS_MISMATCH, (nslotnames, nslotflags)))
98
146
if c. inferred
99
147
nslottypes = length (c. slottypes)
@@ -118,32 +166,50 @@ the `CodeInfo` instance associated with `mi`.
118
166
"""
119
167
function validate_code! (errors:: Vector{>:InvalidCodeError} , mi:: Core.MethodInstance ,
120
168
c:: Union{Void,CodeInfo} = Core. Inference. retrieve_code_info (mi))
121
- m = mi. def:: Method
122
- n_sig_params = length (Core. Inference. unwrap_unionall (m. sig). parameters)
123
- if (m. isva ? (n_sig_params < (m. nargs - 1 )) : (n_sig_params != m. nargs))
124
- push! (errors, InvalidCodeError (SIGNATURE_NARGS_MISMATCH, (m. isva, n_sig_params, m. nargs)))
169
+ is_top_level = mi. def isa Module
170
+ if is_top_level
171
+ mnargs = 0
172
+ else
173
+ m = mi. def:: Method
174
+ mnargs = m. nargs
175
+ n_sig_params = length (Core. Inference. unwrap_unionall (m. sig). parameters)
176
+ if (m. isva ? (n_sig_params < (mnargs - 1 )) : (n_sig_params != mnargs))
177
+ push! (errors, InvalidCodeError (SIGNATURE_NARGS_MISMATCH, (m. isva, n_sig_params, mnargs)))
178
+ end
125
179
end
126
180
if isa (c, CodeInfo)
127
- m . nargs > length (c. slotnames) && push! (errors, InvalidCodeError (SLOTNAMES_NARGS_MISMATCH))
128
- validate_code! (errors, c, m . nargs == 0 )
181
+ mnargs > length (c. slotnames) && push! (errors, InvalidCodeError (SLOTNAMES_NARGS_MISMATCH))
182
+ validate_code! (errors, c, is_top_level )
129
183
end
130
184
return errors
131
185
end
132
186
133
187
validate_code (args... ) = validate_code! (Vector {InvalidCodeError} (), args... )
134
188
135
- function walkast (f, stmts:: Array )
136
- for stmt in stmts
137
- f (stmt)
138
- isa (stmt, Expr) && walkast (f, stmt. args)
189
+ is_valid_lvalue (x) = isa (x, Slot) || isa (x, SSAValue) || isa (x, GlobalRef)
190
+
191
+ function is_valid_argument (x)
192
+ if isa (x, Slot) || isa (x, SSAValue) || isa (x, GlobalRef) || isa (x, QuoteNode) ||
193
+ (isa (x,Expr) && (x. head in (:static_parameter , :boundscheck , :copyast ))) ||
194
+ isa (x, Number) || isa (x, AbstractString) || isa (x, Char) || isa (x, Tuple) ||
195
+ isa (x, Type) || isa (x, Core. Box) || isa (x, Module) || x === nothing
196
+ return true
139
197
end
198
+ # TODO : consider being stricter about what needs to be wrapped with QuoteNode
199
+ return ! (isa (x,Expr) || isa (x,Symbol) || isa (x,GotoNode) || isa (x,LabelNode) ||
200
+ isa (x,LineNumberNode) || isa (x,NewvarNode))
140
201
end
141
202
142
- is_valid_lvalue (x) = isa (x, SlotNumber) || isa (x, SSAValue) || isa (x, GlobalRef)
143
-
144
- function is_valid_rvalue (x)
145
- isa (x, Expr) && return ! in (x. head, (:gotoifnot , :line , :const , :meta ))
146
- return ! isa (x, GotoNode) && ! isa (x, LabelNode) && ! isa (x, LineNumberNode)
203
+ function is_valid_rvalue (lhs, x)
204
+ is_valid_argument (x) && return true
205
+ if isa (x, Expr) && x. head in (:new , :the_exception , :isdefined , :call , :invoke , :foreigncall , :gc_preserve_begin )
206
+ return true
207
+ # TODO : disallow `globalref = call` when .typ field is removed
208
+ # return isa(lhs, SSAValue) || isa(lhs, Slot)
209
+ end
210
+ return false
147
211
end
148
212
213
+ is_valid_return (x) = is_valid_argument (x) || (isa (x,Expr) && x. head in (:new , :lambda ))
214
+
149
215
is_flag_set (byte:: UInt8 , flag:: UInt8 ) = (byte & flag) == flag
0 commit comments