www.digitalmars.com         C & C++   DMDScript  

D - Bug: Co-variance reported with different calling convention

reply "Matthew Wilson" <matthew stlsoft.org> writes:
class X
{
    extern(Windows):
    void x()
    {
        printf("X::x()\n");
    }
}

class Y
    : X
{
    void x()
    {
        printf("Y::x()\n");
    }
}


"combase_test.d(51): function x overrides but is not covariant with x"


I strongly suspect that this is not just an omission, but something that has
been discussed and is a deliberate behaviour. If that is so, can someone
enlighten me as to the rationale?

:)

Matthew
Jul 22 2003
next sibling parent "Matthew Wilson" <matthew stlsoft.org> writes:
 I strongly suspect that this is not just an omission, but something that
has
 been discussed and is a deliberate behaviour. If that is so, can someone
 enlighten me as to the rationale?
Of course, even if it deliberately cannot intuit the calling convention, the error message is still wrong. (Unless one counts co-variance to include calling convention, of course ...)
Jul 22 2003
prev sibling next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I assume the problem is the same as if you had overloaded the function 
with one with a different return value.  The two functions have the same 
calling parameters, but they have different signatures.  A calling 
function that is expecting extern(Windows) but gets the non-extern 
version will break, and vice-versa.

Matthew Wilson wrote:
 class X
 {
     extern(Windows):
     void x()
     {
         printf("X::x()\n");
     }
 }
 
 class Y
     : X
 {
     void x()
     {
         printf("Y::x()\n");
     }
 }
 
 
 "combase_test.d(51): function x overrides but is not covariant with x"
 
 
 I strongly suspect that this is not just an omission, but something that has
 been discussed and is a deliberate behaviour. If that is so, can someone
 enlighten me as to the rationale?
 
 :)
 
 Matthew
 
 
Jul 22 2003
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
I get that. My point was that it either doesn't make sense, or is
arbitrarily strict, depending on one's viewpoint.

Further, this will run into problems with templates, when one wants to use
bolt-ins (which is precisely what I was laying the groundwork for with the
work that precipitated the error), and will thus dramatically complicate the
lives of template library writers.

If anyone can see a significant +ve to the current behaviour, please tell,
and I'll know to abandon this quest, otherwise I'll have to keep banging on
about it until something gives. (That something is, of course, you Walter!
< >)

"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:bfje0h$1jlo$1 digitaldaemon.com...
 I assume the problem is the same as if you had overloaded the function
 with one with a different return value.  The two functions have the same
 calling parameters, but they have different signatures.  A calling
 function that is expecting extern(Windows) but gets the non-extern
 version will break, and vice-versa.

 Matthew Wilson wrote:
 class X
 {
     extern(Windows):
     void x()
     {
         printf("X::x()\n");
     }
 }

 class Y
     : X
 {
     void x()
     {
         printf("Y::x()\n");
     }
 }


 "combase_test.d(51): function x overrides but is not covariant with x"


 I strongly suspect that this is not just an omission, but something that
has
 been discussed and is a deliberate behaviour. If that is so, can someone
 enlighten me as to the rationale?

 :)

 Matthew
Jul 24 2003
parent reply Farmer <itsFarmer. freenet.de> writes:
Hi Matthew,

you wrote:
 class X
 {
     extern(Windows):
     void x()
     {
         printf("X::x()\n");
     }
 }

 class Y
     : X
 {
     void x()
     {
         printf("Y::x()\n");
     }
 }
As you know, any occurence of class X must be substitutable for class Y. This must be true from a designer's perspective *and* from the compiler's view. But due to the different calling convention of X.x() and Y.x(), calling Y.x() instead of X.x() is not possible in a straight way. Yes, the compiler could generate code to make it work (behind your back), but the programmer could easily make that, too.
 Further, this will run into problems with templates, when one wants to
 use bolt-ins (which is precisely what I was laying the groundwork for
 with the work that precipitated the error), and will thus dramatically
 complicate the lives of template library writers.
Have you link at hand for an article about bolt-ins (from you)? I enjoyed that one about shims. Farmer.
Jul 28 2003
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
 Have you link at hand for an article about bolt-ins (from you)?
Alas no. You may have to wait for the book. ;) (I'm doing those chapters at the moment.)
 I enjoyed that one about shims.
Please to hear it.
Jul 29 2003
parent "Matthew Wilson" <matthew stlsoft.org> writes:
 Please to hear it.
Pleased to hear it, I meant
Jul 29 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
The problem is that the calling conventions for X.x and Y.x are different,
yet being virtual, they must have the same calling convention.

"Matthew Wilson" <matthew stlsoft.org> wrote in message
news:bfj6h0$1cbf$1 digitaldaemon.com...
 class X
 {
     extern(Windows):
     void x()
     {
         printf("X::x()\n");
     }
 }

 class Y
     : X
 {
     void x()
     {
         printf("Y::x()\n");
     }
 }


 "combase_test.d(51): function x overrides but is not covariant with x"


 I strongly suspect that this is not just an omission, but something that
has
 been discussed and is a deliberate behaviour. If that is so, can someone
 enlighten me as to the rationale?

 :)

 Matthew
Aug 29 2003
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
Well of course. We need a solution.

I would very much like the following:

1. That Y.x is automatically deduced to be of type extern(Windows). Since D
does not allow non-virtuals, this seems the best way. And consider what
happens when Y is a template! Are we saying some superbly useful veneer
template (to shamelessly use my own terminology) can be restricted to
classes with matching methods of only one calling-convention? That totally
sucks

2. A corollary is that calling convention should be "promoted" to something
more inate in the language, so that I could write a template such as the
following (in pseudo-C++, since my D templating is a little rusty):

    T is assumed to be an interface on which (pure virtual) lock() and
unlock() methods are defined. We want a template that will work with any
calling convention

    template <typename T>
    class ref_counter
      : public T
    {
    public:
      template<callconv C>
      extern (C)
      void C lock()
      {
        . . .
      }
      template<callconv C>
      extern (C)
      void C unlock()
      {
        . . .
      }
    };

Now this works with

    struct ICDeclRefCounter
    {
      virtual void __cdecl lock() = 0;
      virtual void __cdecl unlock() = 0;
    };

    struct IStdCallRefCounter
    {
      virtual void __stdcall lock() = 0;
      virtual void __stdcall unlock() = 0;
    };

etc. etc.

This is the most truculent imperfection in C++, and I f***ing hate it. It
would be a stunning victory for D to handle this in the language proper,
rather than having to use shims, or similar, workarounds.

Nice challenge? ;)


"Walter" <walter digitalmars.com> wrote in message
news:bip6lh$17hg$1 digitaldaemon.com...
 The problem is that the calling conventions for X.x and Y.x are different,
 yet being virtual, they must have the same calling convention.

 "Matthew Wilson" <matthew stlsoft.org> wrote in message
 news:bfj6h0$1cbf$1 digitaldaemon.com...
 class X
 {
     extern(Windows):
     void x()
     {
         printf("X::x()\n");
     }
 }

 class Y
     : X
 {
     void x()
     {
         printf("Y::x()\n");
     }
 }


 "combase_test.d(51): function x overrides but is not covariant with x"


 I strongly suspect that this is not just an omission, but something that
has
 been discussed and is a deliberate behaviour. If that is so, can someone
 enlighten me as to the rationale?

 :)

 Matthew
Aug 29 2003
parent reply "Walter" <walter digitalmars.com> writes:
Converting one calling convention to another would require the compiler to
generate a function to do it, as in:

    int foo(int a, int b, int c)
    {
        int theotherfoo(int a, b, c);
    }

Your item 1 may be the best solution.

"Matthew Wilson" <matthew stlsoft.org> wrote in message
news:bipcba$1g74$1 digitaldaemon.com...
 Well of course. We need a solution.

 I would very much like the following:

 1. That Y.x is automatically deduced to be of type extern(Windows). Since
D
 does not allow non-virtuals, this seems the best way. And consider what
 happens when Y is a template! Are we saying some superbly useful veneer
 template (to shamelessly use my own terminology) can be restricted to
 classes with matching methods of only one calling-convention? That totally
 sucks

 2. A corollary is that calling convention should be "promoted" to
something
 more inate in the language, so that I could write a template such as the
 following (in pseudo-C++, since my D templating is a little rusty):

     T is assumed to be an interface on which (pure virtual) lock() and
 unlock() methods are defined. We want a template that will work with any
 calling convention

     template <typename T>
     class ref_counter
       : public T
     {
     public:
       template<callconv C>
       extern (C)
       void C lock()
       {
         . . .
       }
       template<callconv C>
       extern (C)
       void C unlock()
       {
         . . .
       }
     };

 Now this works with

     struct ICDeclRefCounter
     {
       virtual void __cdecl lock() = 0;
       virtual void __cdecl unlock() = 0;
     };

     struct IStdCallRefCounter
     {
       virtual void __stdcall lock() = 0;
       virtual void __stdcall unlock() = 0;
     };

 etc. etc.

 This is the most truculent imperfection in C++, and I f***ing hate it. It
 would be a stunning victory for D to handle this in the language proper,
 rather than having to use shims, or similar, workarounds.

 Nice challenge? ;)


 "Walter" <walter digitalmars.com> wrote in message
 news:bip6lh$17hg$1 digitaldaemon.com...
 The problem is that the calling conventions for X.x and Y.x are
different,
 yet being virtual, they must have the same calling convention.

 "Matthew Wilson" <matthew stlsoft.org> wrote in message
 news:bfj6h0$1cbf$1 digitaldaemon.com...
 class X
 {
     extern(Windows):
     void x()
     {
         printf("X::x()\n");
     }
 }

 class Y
     : X
 {
     void x()
     {
         printf("Y::x()\n");
     }
 }


 "combase_test.d(51): function x overrides but is not covariant with x"


 I strongly suspect that this is not just an omission, but something
that
 has
 been discussed and is a deliberate behaviour. If that is so, can
someone
 enlighten me as to the rationale?

 :)

 Matthew
Aug 30 2003
parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Walter" <walter digitalmars.com> ha scritto nel messaggio
news:biphsq$1o9r$1 digitaldaemon.com...
 Converting one calling convention to another would require the compiler to
 generate a function to do it, as in:

     int foo(int a, int b, int c)
     {
         int theotherfoo(int a, b, c);
     }

 Your item 1 may be the best solution.
What about performance? Is it maybe better to explicitly enable, or being able to disable, calling performance deduction via a compiler flag? Ric
Sep 01 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Riccardo De Agostini" <riccardo.de.agostini email.it> wrote in message
news:biuv93$nt2$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> ha scritto nel messaggio
 news:biphsq$1o9r$1 digitaldaemon.com...
 Converting one calling convention to another would require the compiler
to
 generate a function to do it, as in:

     int foo(int a, int b, int c)
     {
         int theotherfoo(int a, b, c);
     }

 Your item 1 may be the best solution.
What about performance? Is it maybe better to explicitly enable, or being able to disable, calling performance deduction via a compiler flag?
While that's possible, I'm philosophically opposed to having compiler flags change the semantics of the language. There are innumerable such flags on typical C++ compilers, which cause endless problems.
Sep 01 2003
next sibling parent "Matthew Wilson" <dmd synesis.com.au> writes:
I don't want a compiler flag. I want the language to deduce the right
behaviour for me.

"Walter" <walter digitalmars.com> wrote in message
news:biuvsu$on5$1 digitaldaemon.com...
 "Riccardo De Agostini" <riccardo.de.agostini email.it> wrote in message
 news:biuv93$nt2$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> ha scritto nel messaggio
 news:biphsq$1o9r$1 digitaldaemon.com...
 Converting one calling convention to another would require the
compiler
 to
 generate a function to do it, as in:

     int foo(int a, int b, int c)
     {
         int theotherfoo(int a, b, c);
     }

 Your item 1 may be the best solution.
What about performance? Is it maybe better to explicitly enable, or
being
 able to disable, calling performance deduction via a compiler flag?
While that's possible, I'm philosophically opposed to having compiler
flags
 change the semantics of the language. There are innumerable such flags on
 typical C++ compilers, which cause endless problems.
Sep 01 2003
prev sibling parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Walter" <walter digitalmars.com> ha scritto nel messaggio
news:biuvsu$on5$1 digitaldaemon.com...
 While that's possible, I'm philosophically opposed to having compiler
flags
 change the semantics of the language. There are innumerable such flags on
 typical C++ compilers, which cause endless problems.
I agree on that. How about defining a finite (and very small) set of language feature subsets, thus giving us only one switch to select the desired subset, in order to, for instance, compile for a slower target (typically an embedded system) being sure that what the compiler does under the hood doesn't result in performance loss in critical sections of the code? The need arises, funny enough, from one of the best features in DMD, i.e. being free of the virtual keyword, not to mention calling-convention polymorphism... OTOH, I realize that defining the subsets is a good starting point for holy wars :-) besides being, maybe, unpractical for other reasons. I'd really like feedback on this from people more experienced than I... Ric
Sep 01 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Riccardo De Agostini" <riccardo.de.agostini email.it> wrote in message
news:biv4q0$v71$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> ha scritto nel messaggio
 news:biuvsu$on5$1 digitaldaemon.com...
 While that's possible, I'm philosophically opposed to having compiler
flags
 change the semantics of the language. There are innumerable such flags
on
 typical C++ compilers, which cause endless problems.
I agree on that. How about defining a finite (and very small) set of language feature subsets, thus giving us only one switch to select the desired subset, in order to, for instance, compile for a slower target (typically an embedded system) being sure that what the compiler does
under
 the hood doesn't result in performance loss in critical sections of the
 code?
You can make a function 'final' and it will be non-virtual.
 The need arises, funny enough, from one of the best features in DMD, i.e.
 being free of the virtual keyword, not to mention calling-convention
 polymorphism...
 OTOH, I realize that defining the subsets is a good starting point for
holy
 wars :-) besides being, maybe, unpractical for other reasons. I'd really
 like feedback on this from people more experienced than I...

 Ric
Sep 02 2003
parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Walter" <walter digitalmars.com> ha scritto nel messaggio
news:bj334s$ecp$1 digitaldaemon.com...

 You can make a function 'final' and it will be non-virtual.
While that may be a solution, or better a workaround, I don't think this is the purpose of "final" functions. Anyway, for the sake of consistent language semantics, I think I can live with automatic calling convention deduction after all. :) Ric
Sep 03 2003