SUBTYPEP not working properly on COMPLEX types

Bug #1733436 reported by Paul F. Dietz
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Won't Fix
Undecided
Unassigned

Bug Description

According to the hyperspec for the type specifier (COMPLEX typespec):

"Every element of this type is a complex whose real part and imaginary part are each of type (upgraded-complex-part-type typespec). This type encompasses those complexes that can result by giving numbers of type typespec to complex.

(complex type-specifier) refers to all complexes that can result from giving numbers of type type-specifier to the function complex, plus all other complexes of the same specialized representation."

This means that (COMPLEX <t1>) and (COMPLEX <t2>) are synonyms if UPGRADED-COMPLEX-PART-TYPE is the same for <T1> and <T2>. However:

(upgraded-complex-part-type 'integer) ==> RATIONAL
(upgraded-complex-part-type 'rational) ==> RATIONAL
(subtypep '(complex rational) '(complex integer)) ==> NIL, T

Revision history for this message
Douglas Katzman (dougk) wrote :

wow, that seems really weird.

Our complex numbers give you back a more specific thing that the UPGRADED- query reports that they'd give you back. i.e. (complex 1 1) *is* a complex integer according to TYPE-OF. I think that's the broken-ness is that the UPGRADED- answer is wrong.

In fact, if "(complex type-specifier) refers to all [...] other complexes of the same specialized representation.", then '(COMPLEX INTEGER) should have an an element #C(1/ 1/2), right?
Because the specialized representation is (allegedly) the same.
But (typep (complex 1/2 1/2) '(complex integer)) => NIL
So I think we should say that the specialized representation isn't the same. We should claim in the interface that (complex integer) is not (complex rational)

This also differs from MAKE-ARRAY where you ask for some element type and if you get a different type, it matters, because it tells you what you can *in* *practice* put in the array. But you can't store anything different in the complex number from what it started as, because numbers are immutable. Consider also one of the stated purposes of the array inquiry function: it was to avoid having to do (TYPE-OF (MAKE-ARRAY 1 :element-type 'sometype)) to see how upgrading happens.
This doesn't help COMPLEX, because it doesn't help you know what (COMPLEX 42s0 1/2) returns.

I wonder what would be the harm if we always said that all complex numbers upgrade to (complex real), but internally we make use of more specialized representations? This would make the subtypep logic for complex easier.
(You can get away with misrepresenting what your specialized representations are if there's no way to prove that the specialization is wrong. This would not be true of arrays - if we falsely reported that every array upgrades to element type T, but (MAKE-ARRAY 1 :element-type 'symbol) returned an array that could truly only hold symbols, then the user would be misled if s/he tried to store a non-symbol despite that upgraded-array-element-type said that it was T)

Revision history for this message
Stas Boukarev (stassats) wrote :

The spec is too ambiguous about this, the status quo will remain.

Changed in sbcl:
status: New → Won't Fix
Revision history for this message
Christophe Rhodes (csr21-cantab) wrote : Re: [Bug 1733436] Re: SUBTYPEP not working properly on COMPLEX types

Douglas Katzman <email address hidden> writes:

> I wonder what would be the harm if we always said that all complex
> numbers upgrade to (complex real), but internally we make use of more
> specialized representations? This would make the subtypep logic for
> complex easier.

I suspect that some numerical code likes to be able to declare things as
(complex single-float) and have realpart/imagpart be inferred as
single-float. If (complex single-float) is type-equivalent to (complex
real), then I can pass a (complex integer) to a function whose argument
is declared as (complex single-float), so the compiler can't optimize
based on the declaration. I think.

I'm not sure what the difficulty in having three types -- (complex
rational), (complex single-float) and (complex double-float) -- is?
#c(1/2 1/2) is a (complex rational); it is also a (complex integer) in
our implementation. (The one thing it isn't is a (complex (eql 0))...)

Christophe

Revision history for this message
Paul F. Dietz (paul-f-dietz) wrote :

I'll just add that upgrading also means typep does things you might not expect on arrays, too.

(typep (make-array '(1) :element-type 'fixnum :initial-element -2) '(vector (integer -1 10000000000)))
==>
T

(this is on a 64 bit SBCL)

This is true because (integer -1 10000000000) upgrades to fixnum, so the element types of the two arrays are the same.

Revision history for this message
Paul F. Dietz (paul-f-dietz) wrote :

The optimization goal can be preserved for single-float and double-float if (COMPLEX SINGLE-FLOAT) and (COMPLEX DOUBLE-FLOAT) are constrained to have no proper COMPLEX subtypes (except perhaps for (COMPLEX NIL), which is ok as it is also an empty type.) I believe this is the case in SBCL now.

A root problem here is that type declarations are being used in two not entirely compatible ways: to signal the representation the compiler should use, and to further constrain the values of the real and imaginary parts.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.