1 USING: accessors arrays generic stack-checker
2 stack-checker.backend stack-checker.errors kernel classes
3 kernel.private math math.parser math.private namespaces
4 namespaces.private parser sequences strings vectors words
5 quotations effects tools.test continuations generic.standard
6 sorting assocs definitions prettyprint io inspector
7 classes.tuple classes.union classes.predicate debugger
8 threads.private io.streams.string io.timeouts io.thread
9 sequences.private destructors combinators eval locals.backend
10 system compiler.units shuffle vocabs ;
11 IN: stack-checker.tests
13 [ 1234 infer ] must-fail
15 { 0 2 } [ 2 "Hello" ] must-infer-as
16 { 1 2 } [ dup ] must-infer-as
18 { 1 2 } [ [ dup ] call ] must-infer-as
19 [ [ call ] infer ] [ T{ unknown-macro-input f call } = ] must-fail-with
20 [ [ curry call ] infer ] [ T{ unknown-macro-input f call } = ] must-fail-with
21 [ [ { } >quotation call ] infer ] [ T{ bad-macro-input f call } = ] must-fail-with
22 [ [ append curry call ] infer ] [ T{ bad-macro-input f call } = ] must-fail-with
24 { 2 4 } [ 2dup ] must-infer-as
26 { 1 0 } [ [ ] [ ] if ] must-infer-as
27 [ [ if ] infer ] [ T{ unknown-macro-input f if } = ] must-fail-with
28 [ [ { } >quotation { } >quotation if ] infer ] [ T{ bad-macro-input f if } = ] must-fail-with
29 [ [ [ ] if ] infer ] [ T{ unknown-macro-input f if } = ] must-fail-with
30 [ [ [ 2 ] [ ] if ] infer ] [ unbalanced-branches-error? ] must-fail-with
31 { 4 3 } [ [ rot ] [ -rot ] if ] must-infer-as
35 [ swap 3 ] [ nip 5 5 ] if
41 { 1 1 } [ dup [ ] when ] must-infer-as
42 { 1 1 } [ dup [ dup fixnum* ] when ] must-infer-as
43 { 2 1 } [ [ dup fixnum* ] when ] must-infer-as
45 { 1 0 } [ [ drop ] when* ] must-infer-as
46 { 1 1 } [ [ { { [ ] } } ] unless* ] must-infer-as
49 [ [ 2 2 fixnum+ ] dup [ ] when call ] must-infer-as
52 [ [ [ 2 2 fixnum+ ] ] [ [ 2 2 fixnum* ] ] if call ] infer
53 ] [ T{ bad-macro-input f call } = ] must-fail-with
55 ! Test inference of termination of control flow
56 : termination-test-1 ( -- * ) "foo" throw ;
58 : termination-test-2 ( ? -- x ) [ termination-test-1 ] [ 3 ] if ;
60 { 1 1 } [ termination-test-2 ] must-infer-as
62 : simple-recursion-1 ( obj -- obj )
63 dup [ simple-recursion-1 ] [ ] if ;
65 { 1 1 } [ simple-recursion-1 ] must-infer-as
67 : simple-recursion-2 ( obj -- obj )
68 dup [ ] [ simple-recursion-2 ] if ;
70 { 1 1 } [ simple-recursion-2 ] must-infer-as
72 : funny-recursion ( obj -- obj )
73 dup [ funny-recursion 1 ] [ 2 ] if drop ;
75 { 1 1 } [ funny-recursion ] must-infer-as
78 { 1 2 } [ [ first ] keep second ] must-infer-as
83 : fie ( element obj -- ? )
84 dup array? [ foe ] [ eq? ] if ;
86 : foe ( element tree -- ? )
101 { 2 1 } [ fie ] must-infer-as
102 { 2 1 } [ foe ] must-infer-as
111 { 0 0 } [ nested-when ] must-infer-as
113 : nested-when* ( obj -- )
120 { 1 0 } [ nested-when* ] must-infer-as
124 { 0 1 } [ sym-test ] must-infer-as
126 : terminator-branch ( a -- b )
133 { 1 1 } [ terminator-branch ] must-infer-as
135 : recursive-terminator ( obj -- )
142 { 1 0 } [ recursive-terminator ] must-infer-as
144 GENERIC: potential-hang ( obj -- obj )
145 M: fixnum potential-hang dup [ potential-hang ] when ;
147 [ ] [ [ 5 potential-hang ] infer drop ] unit-test
149 TUPLE: funny-cons car cdr ;
150 GENERIC: iterate ( obj -- )
151 M: funny-cons iterate cdr>> iterate ;
153 M: real iterate drop ;
155 { 1 0 } [ iterate ] must-infer-as
158 : cat ( obj -- * ) dup [ throw ] [ throw ] if ;
159 : dog ( a b c -- ) dup [ cat ] [ 3drop ] if ;
160 { 3 0 } [ dog ] must-infer-as
164 : friend ( a b c -- ) dup [ friend ] [ monkey ] if ;
165 : monkey ( a b c -- ) dup [ 3drop ] [ friend ] if ;
166 { 3 0 } [ friend ] must-infer-as
168 ! Regression -- same as above but we infer the second word first
170 : blah ( a b c -- ) dup [ blah ] [ blah2 ] if ;
171 : blah2 ( a b c -- ) dup [ blah ] [ 3drop ] if ;
172 { 3 0 } [ blah2 ] must-infer-as
177 dup [ blah3 ] [ dup [ blah4 ] [ blah3 ] if ] if ;
179 dup [ blah4 ] [ dup [ 3drop ] [ blah3 ] if ] if ;
180 { 3 0 } [ blah4 ] must-infer-as
183 : bad-combinator ( obj quot: ( -- ) -- )
187 [ dip ] keep swap bad-combinator
188 ] if ; inline recursive
190 [ [ [ 1 ] [ ] bad-combinator ] infer ] must-fail
194 dup string? [ 2array throw ] unless
195 over string? [ 2array throw ] unless
199 : too-deep ( a b -- c )
200 dup [ drop ] [ 2dup too-deep too-deep * ] if ; inline recursive
201 { 2 1 } [ too-deep ] must-infer-as
204 [ [ [ dup call ] dup call ] infer ]
205 [ recursive-quotation-error? ] must-fail-with
207 : m ( q -- ) dup call ; inline
209 [ [ [ m ] m ] infer ] [ recursive-quotation-error? ] must-fail-with
211 : m' ( quot -- ) dup curry call ; inline
213 [ [ [ m' ] m' ] infer ] [ recursive-quotation-error? ] must-fail-with
215 : m'' ( -- q ) [ dup curry ] ; inline
217 : m''' ( -- ) m'' call call ; inline
219 [ [ [ m''' ] m''' ] infer ] [ recursive-quotation-error? ] must-fail-with
221 : m-if ( a b c -- ) t over when ; inline
223 [ [ [ m-if ] m-if ] infer ] [ recursive-quotation-error? ] must-fail-with
225 ! This doesn't hang but it's also an example of the
227 [ [ [ [ drop 3 ] swap call ] dup call ] infer ]
228 [ recursive-quotation-error? ] must-fail-with
230 [ [ 1 drop-locals ] infer ] [ too-many-r>? ] must-fail-with
233 [ [ cleave ] infer ] [ T{ unknown-macro-input f cleave } = ] must-fail-with
235 ! Test some curry stuff
236 { 1 1 } [ 3 [ ] curry 4 [ ] curry if ] must-infer-as
237 { 3 1 } [ [ ] curry [ [ ] curry ] dip if ] must-infer-as
239 { 2 1 } [ [ ] curry 4 [ ] curry if ] must-infer-as
241 [ [ 3 [ ] curry 1 2 [ ] 2curry if ] infer ] [ unbalanced-branches-error? ] must-fail-with
242 [ [ [ ] curry [ [ ] 2curry ] dip if ] infer ] [ unbalanced-branches-error? ] must-fail-with
244 { 1 3 } [ [ 2drop f ] assoc-find ] must-infer-as
246 ! Test words with continuations
247 { 0 0 } [ [ drop ] callcc0 ] must-infer-as
248 { 0 1 } [ [ 4 swap continue-with ] callcc1 ] must-infer-as
249 { 2 1 } [ [ + ] [ ] [ ] cleanup ] must-infer-as
250 { 2 1 } [ [ + ] [ 3drop 0 ] recover ] must-infer-as
253 { 1 0 } [ { [ ] } dispatch ] must-infer-as
255 DEFER: inline-recursive-2
256 : inline-recursive-1 ( -- ) inline-recursive-2 ;
257 : inline-recursive-2 ( -- ) inline-recursive-1 ;
259 { 0 0 } [ inline-recursive-1 ] must-infer-as
263 HOOK: my-hook my-var ( -- x )
265 M: integer my-hook "an integer" ;
266 M: string my-hook "a string" ;
268 { 0 1 } [ my-hook ] must-infer-as
272 { 1 1 } [ [ deferred-word ] [ 3 ] if ] must-infer-as
274 DEFER: an-inline-word
276 : normal-word-3 ( -- )
277 3 [ [ 2 + ] curry ] an-inline-word call drop ;
279 : normal-word-2 ( -- )
282 : normal-word ( x -- x )
283 dup [ normal-word-2 ] when ;
285 : an-inline-word ( obj quot -- )
286 [ normal-word ] dip call ; inline
288 { 1 1 } [ [ 3 * ] an-inline-word ] must-infer-as
290 { 0 1 } [ [ 2 ] [ 2 ] [ + ] compose compose call ] must-infer-as
292 ERROR: custom-error ;
294 [ T{ effect f { } { } t } ] [
295 [ custom-error ] infer
298 : funny-throw ( a -- * ) throw ; inline
300 [ T{ effect f { } { } t } ] [
301 [ 3 funny-throw ] infer
304 [ T{ effect f { } { } t } ] [
305 [ custom-error inference-error ] infer
308 [ T{ effect f { "x" } { "x" "x" } t } ] [
309 [ dup [ 3 throw ] dip ] infer
313 [ [ 1 load-locals ] infer ] [ too-many->r? ] must-fail-with
316 [ [ [ f dup ] [ dup ] produce ] infer ] must-fail
318 [ [ [ f dup ] [ ] while ] infer ] must-fail
320 : erg's-inference-bug ( -- ) f dup [ erg's-inference-bug ] when ; inline recursive
321 [ [ erg's-inference-bug ] infer ] must-fail
322 FORGET: erg's-inference-bug
324 : bad-recursion-3 ( -- ) dup [ [ bad-recursion-3 ] dip ] when ; inline recursive
325 [ [ bad-recursion-3 ] infer ] must-fail
326 FORGET: bad-recursion-3
328 : bad-recursion-4 ( -- ) 4 [ dup call [ rot ] dip swap ] times ; inline recursive
329 [ [ [ ] [ 1 2 3 ] over dup bad-recursion-4 ] infer ] must-fail
331 : bad-recursion-5 ( obj quot: ( -- ) -- ) dup call swap bad-recursion-5 ; inline recursive
332 [ [ f [ ] bad-recursion-5 ] infer ] must-fail
334 : bad-recursion-6 ( quot: ( -- ) -- )
335 dup bad-recursion-6 call ; inline recursive
336 [ [ [ drop f ] bad-recursion-6 ] infer ] must-fail
338 [ ] [ [ \ bad-recursion-6 forget ] with-compilation-unit ] unit-test
340 { 3 0 } [ [ 2drop "A" throw ] [ ] if 2drop ] must-infer-as
341 { 2 0 } [ drop f f [ 2drop "A" throw ] [ ] if 2drop ] must-infer-as
343 : unbalanced-retain-usage ( a b -- )
344 dup 10 < [ 2drop 5 1 + unbalanced-retain-usage ] [ 2drop ] if ;
347 [ [ unbalanced-retain-usage ] infer ] [ inference-error? ] must-fail-with
349 FORGET: unbalanced-retain-usage
352 : ddd' ( ? -- ) [ f eee' ] when ; inline recursive
353 : eee' ( ? -- ) [ swap [ ] ] dip ddd' call ; inline recursive
355 [ [ eee' ] infer ] [ inference-error? ] must-fail-with
357 [ ] [ [ \ ddd' forget ] with-compilation-unit ] unit-test
358 [ ] [ [ \ eee' forget ] with-compilation-unit ] unit-test
360 : bogus-error ( x -- )
361 dup "A" throw [ bogus-error ] [ drop ] if ; inline recursive
363 [ bogus-error ] must-infer
365 [ [ clear ] infer. ] [ inference-error? ] must-fail-with
367 : debugging-curry-folding ( quot -- )
368 [ debugging-curry-folding ] curry call ; inline recursive
370 [ [ ] debugging-curry-folding ] must-infer
372 [ [ exit ] [ 1 2 3 ] if ] must-infer
374 ! Stack effects are required now but FORGET: clears them...
375 : forget-test ( -- ) ;
377 [ forget-test ] must-infer
378 [ ] [ [ \ forget-test forget ] with-compilation-unit ] unit-test
379 [ forget-test ] must-infer
381 [ [ cond ] infer ] [ T{ unknown-macro-input f cond } = ] must-fail-with
382 [ [ bi ] infer ] [ T{ unknown-macro-input f call } = ] must-fail-with
384 [ [ each ] infer ] [ T{ unknown-macro-input f each } = ] must-fail-with
385 [ [ if* ] infer ] [ T{ unknown-macro-input f if* } = ] must-fail-with
386 [ [ [ "derp" ] if* ] infer ] [ T{ unknown-macro-input f if* } = ] must-fail-with
388 [ [ [ "OOPS" throw ] dip ] [ drop ] if ] must-infer
390 ! Found during code review
391 [ [ [ drop [ ] ] when call ] infer ] must-fail
392 [ swap [ [ drop [ ] ] when call ] infer ] must-fail
394 { 3 1 } [ call( a b -- c ) ] must-infer-as
395 { 3 1 } [ execute( a b -- c ) ] must-infer-as
397 [ [ call-effect ] infer ] [ T{ unknown-macro-input f call-effect } = ] must-fail-with
398 [ [ execute-effect ] infer ] [ T{ unknown-macro-input f execute-effect } = ] must-fail-with
400 [ \ set-datastack def>> infer ] [ T{ do-not-compile f do-primitive } = ] must-fail-with
401 [ ] [ [ \ set-datastack def>> infer ] try ] unit-test
403 ! Make sure all primitives are covered
405 all-words [ primitive? ] filter
406 [ "default-output-classes" word-prop not ] filter
407 [ "special" word-prop not ] filter
408 [ "shuffle" word-prop not ] filter
411 { 1 0 } [ [ drop ] each ] must-infer-as
412 { 2 1 } [ [ append ] each ] must-infer-as
413 { 1 1 } [ [ ] map ] must-infer-as
414 { 1 1 } [ [ reverse ] map ] must-infer-as
415 { 2 2 } [ [ append dup ] map ] must-infer-as
416 { 2 2 } [ [ swap nth suffix dup ] map-index ] must-infer-as
418 { 4 1 } [ [ 2drop ] [ 2nip ] if ] must-infer-as
419 { 3 3 } [ [ dup ] [ over ] if ] must-infer-as
420 { 1 1 } [ [ 1 ] [ 0 ] if ] must-infer-as
421 { 2 2 } [ [ t ] [ 1 + f ] if ] must-infer-as
423 { 1 0 } [ [ write ] [ "(f)" write ] if* ] must-infer-as
424 { 1 1 } [ [ ] [ f ] if* ] must-infer-as
425 { 2 1 } [ [ nip ] [ drop f ] if* ] must-infer-as
426 { 2 1 } [ [ nip ] [ ] if* ] must-infer-as
427 { 3 2 } [ [ 3append f ] [ ] if* ] must-infer-as
428 { 1 0 } [ [ drop ] [ ] if* ] must-infer-as
430 { 1 1 } [ [ 1 + ] [ "oops" throw ] if* ] must-infer-as
432 : strict-each ( seq quot: ( x -- ) -- )
434 : strict-map ( seq quot: ( x -- x' ) -- seq' )
436 : strict-2map ( xs ys quot: ( x y -- z ) -- zs )
439 { 1 0 } [ [ drop ] strict-each ] must-infer-as
440 { 1 1 } [ [ 1 + ] strict-map ] must-infer-as
441 { 1 1 } [ [ ] strict-map ] must-infer-as
442 { 2 1 } [ [ + ] strict-2map ] must-infer-as
443 { 2 1 } [ [ drop ] strict-2map ] must-infer-as
444 [ [ [ append ] strict-each ] infer ] [ unbalanced-branches-error? ] must-fail-with
445 [ [ [ 1 + ] strict-2map ] infer ] [ unbalanced-branches-error? ] must-fail-with
447 ! ensure that polymorphic checking works on recursive combinators
448 FROM: splitting.private => split, ;
449 { 2 0 } [ [ member? ] curry split, ] must-infer-as
451 [ [ [ write write ] each ] infer ] [ unbalanced-branches-error? ] must-fail-with
453 [ [ [ ] each ] infer ] [ unbalanced-branches-error? ] must-fail-with
454 [ [ [ dup ] map ] infer ] [ unbalanced-branches-error? ] must-fail-with
455 [ [ [ drop ] map ] infer ] [ unbalanced-branches-error? ] must-fail-with
456 [ [ [ 1 + ] map-index ] infer ] [ unbalanced-branches-error? ] must-fail-with
458 [ [ [ dup ] [ ] if ] infer ] [ unbalanced-branches-error? ] must-fail-with
459 [ [ [ 2dup ] [ over ] if ] infer ] [ unbalanced-branches-error? ] must-fail-with
460 [ [ [ drop ] [ ] if ] infer ] [ unbalanced-branches-error? ] must-fail-with
462 [ [ [ ] [ ] if* ] infer ] [ unbalanced-branches-error? ] must-fail-with
463 [ [ [ dup ] [ ] if* ] infer ] [ unbalanced-branches-error? ] must-fail-with
464 [ [ [ drop ] [ drop ] if* ] infer ] [ unbalanced-branches-error? ] must-fail-with
465 [ [ [ ] [ drop ] if* ] infer ] [ unbalanced-branches-error? ] must-fail-with
466 [ [ [ ] [ 2dup ] if* ] infer ] [ unbalanced-branches-error? ] must-fail-with
468 ! M\ declared-effect infer-call* didn't properly unify branches
469 { 1 0 } [ [ 1 [ drop ] [ drop ] if ] each ] must-infer-as