[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: better error messages through assertions
From: |
Andy Wingo |
Subject: |
Re: better error messages through assertions |
Date: |
Wed, 30 Mar 2022 15:28:03 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) |
On Wed 30 Mar 2022 11:37, Ludovic Courtès <ludo@gnu.org> writes:
>> scheme@(guile-user)> (container-contents '())
>> ice-9/boot-9.scm:1685:16: In procedure raise-exception:
>> In procedure struct-vtable: Wrong type argument in position 1
> scheme@(guile-user)> ,use(srfi srfi-9)
> scheme@(guile-user)> (define-record-type <foo>
> (make-foo x)
> foo?
> (x foo-x))
> scheme@(guile-user)> ,optimize (foo-x '())
> $9 = (if (eq? (struct-vtable '()) <foo>)
> (struct-ref '() 0)
> (throw 'wrong-type-arg
> 'foo-x
> "Wrong type argument: ~S"
> (list '())
> (list '())))
>
> With Guile 3, it might be that adding an extra ‘struct?’ test would have
> little effect on performance; we’d need to check.
Would have no effect.
Incidentally, you might want to use ,optimize-cps;
scheme@(guile-user)> ,optimize (foo-x '())
$9 = (if (eq? (struct-vtable '()) <foo>)
(struct-ref '() 0)
(throw 'wrong-type-arg
'foo-x
"Wrong type argument: ~S"
(list '())
(list '())))
scheme@(guile-user)> ,optimize-cps (foo-x '())
L0: ; at
<unknown>:15:14
v0 := self
L1(...)
L1:
receive()
v1 := const () ; arg at
<unknown>:15:21
throw throw/value+data[#(wrong-type-arg "struct-vtable" "Wrong type
argument in position 1 (expecting struct): ~S")](v1) ; at <unknown>:15:14
L1(...) means, pass all values to L1. In this case because there are
varargs on the stack from the procedure call. L1 parses them with the
receive(). Anyway, here we see that with respect to the immediate '(),
that all the tests folded. If we instead lift to a procedure:
scheme@(guile-user)> ,optimize-cps (lambda (x) (foo-x x))
L0: ; at
<unknown>:16:14
v0 := self
L1(...)
L1:
receive()
v1 := current-module() ; module at
<unknown>:16:14
cache-set![0](v1) ; at
<unknown>:16:14
v2 := const-fun L7 ; _
return v2 ; at
<unknown>:16:14
L7: ; at
<unknown>:16:14
v3 := self
L8(...)
L8:
v4 := receive(x) ; x
heap-object?(v4) ? L10() : L38() ; at
<unknown>:16:26
L10():
struct?(v4) ? L11() : L38() ; at
<unknown>:16:26
L38():
throw throw/value+data[#(wrong-type-arg "struct-vtable" "Wrong type
argument in position 1 (expecting struct): ~S")](v4) ; at <unknown>:16:26
L11():
v5 := scm-ref/tag[struct](v4) ; vtable at
<unknown>:16:26
v6 := cache-ref[(0 . <foo>)]() ; cached at
<unknown>:10:20
heap-object?(v6) ? L19() : L14() ; at
<unknown>:10:20
L19():
L20(v6) ; at
<unknown>:10:20
L14():
v7 := cache-ref[0]() ; mod at
<unknown>:10:20
v8 := const <foo> ; name at
<unknown>:10:20
v9 := lookup-bound(v7, v8) ; var at
<unknown>:10:20
cache-set![(0 . <foo>)](v9) ; at
<unknown>:10:20
L20(v9) ; at
<unknown>:10:20
L20(v10): ; box
v11 := scm-ref/immediate[(box . 1)](v10) ; arg at
<unknown>:10:20
eq?(v5, v11) ? L22() : L37() ; at
<unknown>:16:26
L37():
throw throw/value+data[#(wrong-type-arg foo-x "Wrong type argument:
~S")](v4) ; at <unknown>:16:26
L22():
v12 := word-ref/immediate[(struct . 6)](v5) ; rfields at
<unknown>:16:26
v13 := v12 ; nfields at
<unknown>:16:26
imm-u64-<[0](v13) ? L25() : L35() ; at
<unknown>:16:26
L35():
v21 := const 0 ; _ at
<unknown>:16:26
throw throw/value+data[#(out-of-range "struct-ref/immediate" "Argument 2
out of range: ~S")](v21) ; at <unknown>:16:26
L25():
v14 := pointer-ref/immediate[(struct . 7)](v5) ; ptr at
<unknown>:16:26
v15 := load-u64[0]() ; word at
<unknown>:16:26
v16 := u32-ref[bitmask](v5, v14, v15) ; bits at
<unknown>:16:26
v17 := load-u64[1]() ; mask at
<unknown>:16:26
v18 := ulogand(v17, v16) ; res at
<unknown>:16:26
u64-imm-=[0](v18) ? L31() : L33() ; at
<unknown>:16:26
L33():
v20 := const 0 ; _ at
<unknown>:16:26
throw throw/value+data[#(wrong-type-arg "struct-ref/immediate" "Wrong type
argument in position 2 (expecting boxed field): ~S")](v20) ; at <unknown>:16:26
L31():
v19 := scm-ref/immediate[(struct . 1)](v4) ; val at
<unknown>:16:26
return v19 ; at
<unknown>:16:26
Here we see the first procedure which is the thunk that wraps the
expression. Then in the beginning of the procedure at L7 you can see
there is a check for struct?, which has to be dominated by a true
heap-object? check. Duplicate checks are elided. So if SRFI-9 added a
`struct?` check it wouldn't be more code; rather it would be less,
actually, because instead of branching to L38, you'd branch to L37.
Too bad about all that other crap about checking whether the index is in
range and the field is boxed or not, though :-/ Probably there is a
better design...
Andy