May 02, 2024

On Thursday, 2 May 2024 at 17:25:55 UTC, user1234 wrote:

>

On Thursday, 2 May 2024 at 16:25:15 UTC, DrDread wrote:

>

I'm for not having types promote at all. it's been an endless source of bugs for me. it messes with metaprogramming.

PLs that dont promote have their own problems too. The most obvious is that overflowing is more easy. At least promotion mitigates that.

However I'm quite sure that D promotions rules were not seen as such. It's more C compatibility, walking on the C tracks, to speak metaphorically.

Just remembered, one argument that was once exposed by Walter is be that arithmetic instructions for 32 bits registers would be faster than the ones let's say for 16 or 8.

May 02, 2024

On Thursday, 2 May 2024 at 17:25:55 UTC, user1234 wrote:

>

On Thursday, 2 May 2024 at 16:25:15 UTC, DrDread wrote:

>

I'm for not having types promote at all. it's been an endless source of bugs for me. it messes with metaprogramming.

PLs that dont promote have their own problems too. The most obvious is that overflowing is more easy. At least promotion mitigates that.

However I'm quite sure that D promotions rules were not seen as such. It's more C compatibility, walking on the C tracks, to speak metaphorically.

C++ actually gets it right(!)

#include <cstdint>
#include <cstddef>
#include <cstdio>

int main(void)
{
    uint8_t a = {};
    const uint8_t b = {};
    const uint16_t c = {};

    auto x = 1 ? a : b;
    auto y = 1 ? a : c;

    fprintf(stderr, "### %zu\n", sizeof(x)); // 1
    fprintf(stderr, "### %zu\n", sizeof(y)); // 4
}
May 02, 2024

On Thursday, 2 May 2024 at 18:09:56 UTC, Daniel N wrote:

>

On Thursday, 2 May 2024 at 17:25:55 UTC, user1234 wrote:

>

On Thursday, 2 May 2024 at 16:25:15 UTC, DrDread wrote:

>

[...]

PLs that dont promote have their own problems too. The most obvious is that overflowing is more easy. At least promotion mitigates that.

However I'm quite sure that D promotions rules were not seen as such. It's more C compatibility, walking on the C tracks, to speak metaphorically.

C++ actually gets it right(!)

#include <cstdint>
#include <cstddef>
#include <cstdio>

int main(void)
{
    uint8_t a = {};
    const uint8_t b = {};
    const uint16_t c = {};

    auto x = 1 ? a : b;
    auto y = 1 ? a : c;

    fprintf(stderr, "### %zu\n", sizeof(x)); // 1
    fprintf(stderr, "### %zu\n", sizeof(y)); // 4
}

I see, c++ promotes, but differently. D specs should be revised. Probably the rules mentioned earlier, those from ANdrei, should be sorted differently.

May 03, 2024

On Thursday, 2 May 2024 at 17:30:12 UTC, user1234 wrote:

>

Just remembered, one argument that was once exposed by Walter is be that arithmetic instructions for 32 bits registers would be faster than the ones let's say for 16 or 8.

Working on the processor word size is always the fastest. But that is totally independent of the result type. Calculation can always be done extended to word-size (or may need multiple words for large operands), but the result should have the common type.
To say 32bit is always the best is only true for 32bit architectures.
And having no promotion is not an option. If two operands are to be combined, we need some common type for the result, no matter how this result is produced.

About type attributes - the operands may be mutable, const or immutable, but the result is a new value with attributes independent of the operand attributes. It should be assignable to mutable variables - to const or immutable objects only during initialization, to shared objects only if they are locked.

At least that is what I expect.

May 03, 2024
On 03/05/2024 6:52 PM, Dom DiSc wrote:
> On Thursday, 2 May 2024 at 17:30:12 UTC, user1234 wrote:
> 
>     Just remembered, one argument that was once exposed by Walter is be
>     that arithmetic instructions for 32 bits registers would be faster
>     than the ones let's say for 16 or 8.
> 
> Working on the processor word size is always the fastest. But that is totally independent of the result type. Calculation can always be done extended to word-size (or may need multiple words for large operands), but the result should have the common type. To say 32bit is always the best is only true for 32bit architectures. And having no promotion is not an option. If two operands are to be combined, we need some common type for the result, no matter how this result is produced.

Normally for x86 32/64bit are equal in speed but 8/16bit are not very far behind these days.

Due to C its pretty safe to assume that 32bit registers will be pretty heavily optimized for 32bit or above processes.

Consider the Ice Lake series, which last had releases in 2020.

DIV IDIV	r8	4	4
DIV IDIV	r16	4	4
DIV IDIV	r32	4	4
DIV IDIV	r64	4	4

Now compare that to Haswell architecture from 10 years ago:

DIV	r8	9	9
DIV	r16	11	11
DIV	r32	10	10
DIV	r64	36	36
IDIV	r8	9	9
IDIV	r16	10	10
IDIV	r32	9	9
IDIV	r64	59	59

If you don't know the exact target CPU sticking with 32bit is still a good recommendation for CPU's that are 32bit or above.

On the other hand if you know its more recent (say running Windows 11), just use whatever you need and don't worry about it.
May 03, 2024

On Friday, 3 May 2024 at 06:52:00 UTC, Dom DiSc wrote:

>

On Thursday, 2 May 2024 at 17:30:12 UTC, user1234 wrote:

>

Just remembered, one argument that was once exposed by Walter is be that arithmetic instructions for 32 bits registers would be faster than the ones let's say for 16 or 8.

Working on the processor word size is always the fastest. But that is totally independent of the result type. Calculation can always be done extended to word-size (or may need multiple words for large operands), but the result should have the common type.
To say 32bit is always the best is only true for 32bit architectures.
And having no promotion is not an option. If two operands are to be combined, we need some common type for the result, no matter how this result is produced.

About type attributes - the operands may be mutable, const or immutable, but the result is a new value with attributes independent of the operand attributes. It should be assignable to mutable variables - to const or immutable objects only during initialization, to shared objects only if they are locked.

At least that is what I expect.

just force the user to cast when the types are of incompatible sizes instead of promoting them to some arbitrary type.
if the user uses smaller types, they generally do it for size reasons, and don't want to end up with a bigger type.
you normally shouldn't use types <= int32, so it should not be an issue in practice.
what is an issue however is the compiler picking the wrong templates because of some promotion rules.

May 03, 2024
On Friday, 3 May 2024 at 07:05:21 UTC, Richard (Rikki) Andrew Cattermole wrote:
>
> Due to C its pretty safe to assume that 32bit registers will be pretty heavily optimized for 32bit or above processes.
>
> Consider the Ice Lake series, which last had releases in 2020.
>
> DIV IDIV	r8	4	4
> DIV IDIV	r16	4	4
> DIV IDIV	r32	4	4
> DIV IDIV	r64	4	4
>
> Now compare that to Haswell architecture from 10 years ago:
>
> DIV	r8	9	9
> DIV	r16	11	11
> DIV	r32	10	10
> DIV	r64	36	36
> IDIV	r8	9	9
> IDIV	r16	10	10
> IDIV	r32	9	9
> IDIV	r64	59	59
>
> If you don't know the exact target CPU sticking with 32bit is still a good recommendation for CPU's that are 32bit or above.
>
> On the other hand if you know its more recent (say running Windows 11), just use whatever you need and don't worry about it.

That's an interesting data-point, however x86 is unusual in that is supports all register sizes, on other CPUs you have to manually mask the result.

May 03, 2024

On Friday, 3 May 2024 at 06:52:00 UTC, Dom DiSc wrote:

>

On Thursday, 2 May 2024 at 17:30:12 UTC, user1234 wrote:

>

Just remembered, one argument that was once exposed by Walter is be that arithmetic instructions for 32 bits registers would be faster than the ones let's say for 16 or 8.

Working on the processor word size is always the fastest. But that is totally independent of the result type. Calculation can always be done extended to word-size (or may need multiple words for large operands), but the result should have the common type.
To say 32bit is always the best is only true for 32bit architectures.
And having no promotion is not an option. If two operands are to be combined, we need some common type for the result, no matter how this result is produced.

This is all very interesting, but remember, there is no operation or calculation happening here. We have a construct that is picking an 8 bit unsigned value, or an 8 bit unsigned value, and it is resulting in a 32-bit signed value.

If this kind of thing requires integer promotion then the following should be true:

ubyte x = 5;
auto y = x;
static assert(is(typeof(y) == int));

But that is not the case. I don't mean to be pedantic here, but this is the inconsistency I see in this part of the compiler.

>

About type attributes - the operands may be mutable, const or immutable, but the result is a new value with attributes independent of the operand attributes. It should be assignable to mutable variables - to const or immutable objects only during initialization, to shared objects only if they are locked.

Common type modifier differences can be tested with int, and I believe the rules are sound there.

-Steve

May 03, 2024
On 5/1/2024 7:42 AM, Steven Schveighoffer wrote:
> It seems rule 2 would apply instead of rule 6? but I don't like it.

```
#include <stdio.h>

void main()
{
    char u;
    const char v;
    printf("%ld %ld\n", sizeof(u), sizeof(1?u:v));
}
```

This prints "1 4". D follows the same integral promotion rules, and the reason is if one translates C code to D, one doesn't get an unpleasant hidden surprise.
May 03, 2024
On Friday, 3 May 2024 at 19:40:36 UTC, Walter Bright wrote:
> On 5/1/2024 7:42 AM, Steven Schveighoffer wrote:
>> It seems rule 2 would apply instead of rule 6? but I don't like it.
>
> ```
> #include <stdio.h>
>
> void main()
> {
>     char u;
>     const char v;
>     printf("%ld %ld\n", sizeof(u), sizeof(1?u:v));
> }
> ```
>
> This prints "1 4". D follows the same integral promotion rules, and the reason is if one translates C code to D, one doesn't get an unpleasant hidden surprise.

Like this D code:

import std.stdio;

void main(){
    char u;
    const char v;
    writefln("%d %d", (u.sizeof), (1?u:v).sizeof);
}

Prints: "1 1".

=]

Matheus.