Here's another slightly dodgy type system patch that could probably do
I thought that I would attack the remaining major type system bug (bug
91, described in BUGS as
(subtypep '(or (integer -1 1)
'(or (rational -1 7)
(integer -1 1))) => NIL, T
This I felt was especially bad, as we weren't just expressing
uncertainty, we were returning a wrong result.
Initial investigations revealed that this was slightly misleading, as
(sb-kernel::specifier-type '(or (integer -1 1) unsigned-byte))
is canonicalized to #<NUMERIC-TYPE (INTEGER -1)>. So it's not just
simple unions that were the problem.
Some of the discussion in the thread referred to in BUG 91 ("bug in type
handling") pointed me in the right direction; canonicalization of the
other type in the subtypep form above can happen in two ways, depending
on whether the union of (rational -1 7) (integer -1 1) or (integer -1 1)
unsigned-byte is taken first. This, coupled with the fact that the union
:complex-subtypep-arg2 method [ essentially
(any/type #'csubtypep type1 (union-type-types type2)) ]
was a sufficient but not necessary condition for subtypepness caused us
to give wrong results in the above case.
So, I've reimplemented union-complex-subtypep-arg2 in a more
mathematically correct way. I've commented the (attached) patch fairly
carefully, as "mathematically correct" unfortunately doesn't mean
Somewhat unsurprisingly, this change exposed a number of assumptions and
infelicities in the rest of the type system :-/. Some notes:
The change in cross-type.lisp should probably be dealt with first. At
some point in make-host-2, during the compilation of (I think)
host-alieneval.lisp, the host tries to work out
(subtypep 'null '(or (and null (complex double-float))
(and null (complex single-float))))
Normal canonicalization of intersection types will make the latter type
equal to NIL, but as a consequence of another change the cross-compiler
couldn't work that out. Unfortunately I can't remember what change it
The late-type patches, in order:
1. We canonicalize type-union2 earlier when the type system knows that
one type is a subset of the other.
2. The next is a FIXME note regarding type-intersection; eq appears not
to catch all type specifiers. I haven't investigated this in any more
3. Next, because of some more call-next-methodish invocations, the
internals of the type system now can ask complex-subtypep-arg1 whether
*wild-type* is subtypep. Therefore, I've removed the averrance, and
adjusted the logic in the type method.
4. In the HAIRY intersection method, I've adjusted it to check if the
types are equal. This isn't ideal (see 2) but I think it'll do for now.
This is necessary (I think) because src/compiler/node.lisp is compiling
with a forward reference to a type; see the INFO slot in the definition
of the COMPONENT structure.
5. In our brave new world of clever union subtype handling, we also need
to be clever about union type=. Again, we previously had a sufficient
but not necessary condition (that the union-type-types of the two union
types were the same, in some order). Also, we want to avoid returning
certainty when we shouldn't, so better return NIL NIL when a union
containing a hairy-type is tested for equality. Finally in this hunk we
get the meat of the patch (8 lines of code and 25 of comment...)
6. The UNION :intersection2 methods also need a bit of smartening up; in
the UNION :subtypep methods, we have used the fact that the complex
methods test a UNION type against a non-UNION, and now the averrances
catch that, so we ensure that we call only the applicable methods.
(Arguably, this might be cleaner if it were split into separate UNION
:simple-intersection2 and UNION :complex-intersection2 methods, with the
accumulator logic in a separate function. Discuss...)
That's all, folks, apart from a request for clarification in
src/code/typedefs.lisp and some extra bonus tests!
As you can gather from this, there were (again) some nasty surprises
along the path; effort and tinkering were required both to get it to
compile, and then to get it to be able to compile itself. It has now
done both, and I'm reasonably happy with it. Thoughts?
PS: Is there any chance of having BUG defined on the host, too?
Otherwise failing AVERs during the compilation doesn't print out a
helpful message but only "ERROR: undefined function BUG" :-/