Repository: go
Issue: spec: abandon remaining ambiguity in definition of integer and floating point types 
Labels: Documentation,NeedsInvestigation

@griesemer
EDIT: Significantly improved the description of the issue.
## Introduction 

In issue #49804 I pointed out the the terms integer type, floating-point type and complex type were not defined, but used in several places in the language specification in order to define other terms. The missing definition lead to ambiguity, most notably about which types are permissible for array or slice indices. For example the go compiler did accept indexing by `uint32` while the officially recommended debugger dlv did not recognize indexing by uint32 when using the command `print`.

In discussion of issue #49804 the necessity to define the terms was acknowledged as a known problem. The draft of the specification for go version 1.18 now introduces these terms explicitly. Accordingly, issue  #49804 was closed. However, exactly the same ambiguity that existed before is now encapsulated in the new and explicit definition of the terms integer type, floating-point type and complex type as of the draft of go version 1.18.

This issue differs from issue #49804 in so far as in this issue I provide a specific proposal on how the ambiguity can be resolved properly. 

## Short and informal description of the problem and the proposed changes

### A short explanation of the problem

In the current version the terms integer type, floating-point type and complex type are introduced in the section about numeric types with the sentence:

> An <i>integer</i>, <i>floating-point</i>, or <i>complex</i> type represents the set of integer, floating-point, or complex values, respectively. They are collectively called <i>numeric types</i>.

Then in the same section, the predeclared architecture-independent and architecture-dependent numeric types are introduced. The problem is that it is nowhere explicitly stated which of these are of integer-type, of floating-point type or of complex type.

Even novice programmers might intuitively assume that for example `complex64` is not of integer type, and `uint32` not of floating-point type. But less experienced programmers, or programmers for whom writing safe code is of concern might reasonably assume, that for example indexing by any non-`int` type is a feature of the compiler and not of the language itself.

Actually, it is not even clearly stated that these classes do do not overlap, i.e. it is not clear that a type of integer type cannot simultaneously be of floating-point type.

### A short explanation of the proposed changes

I propose to rewrite the section about numeric types. Instead of introducing the predeclared numeric types all together I propose to introduce the predeclared integer, floating-point and complex types individually.

## Explicit wording of the current and proposed version 

### Current version of the section about numeric types
I propose to change the current version of the section about numeric types, which is:
```
<h3 id="Numeric_types">Numeric types</h3>

<p>
An <i>integer</i>, <i>floating-point</i>, or <i>complex</i> type
represents the set of integer, floating-point, or complex values, respectively.
They are collectively called <i>numeric types</i>.
The predeclared architecture-independent numeric types are:
</p>

<pre class="grammar">
uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32
</pre>

<p>
The value of an <i>n</i>-bit integer is <i>n</i> bits wide and represented using
<a href="https://en.wikipedia.org/wiki/Two's_complement">two's complement arithmetic</a>.
</p>

<p>
There is also a set of predeclared integer types with implementation-specific sizes:
</p>

<pre class="grammar">
uint     either 32 or 64 bits
int      same size as uint
uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value
</pre>

<p>
To avoid portability issues all numeric types are <a href="#Type_definitions">defined
types</a> and thus distinct except
<code>byte</code>, which is an <a href="#Alias_declarations">alias</a> for <code>uint8</code>, and
<code>rune</code>, which is an alias for <code>int32</code>.
Explicit conversions
are required when different numeric types are mixed in an expression
or assignment. For instance, <code>int32</code> and <code>int</code>
are not the same type even though they may have the same size on a
particular architecture.
```
### The version of the section about numeric types including the changes that I propose

I propose to change that section to include the following instead:
```
<h3 id="Numeric_types">Numeric types</h3>

<p>
A <i>numeric type</i> is a type whose underlying type is 
either of  <i>Integer</i>, <i>floating-point</i>, or <i>complex</i> type.
<i>Integer</i>, <i>floating-point</i>, or <i>complex</i> types 
represent the set of integer, floating-point, or complex values, respectively.
Some predeclared integer types are architecture-dependent.

The following architecture-independent integer types are predeclared:
</p>

<pre class="grammar">
uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

byte        alias for uint8
rune        alias for int32
</pre>
<p>
The value of an <i>n</i>-bit integer is <i>n</i> bits wide and represented using
<a href="https://en.wikipedia.org/wiki/Two's_complement">two's complement arithmetic</a>.
</p>
<p>
The following implementation-specific integer types are predeclared:
</p>
<pre class="grammar">
uint     either 32 or 64 bits
int      same size as uint
uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value
</pre>

The predeclared floating-point types are:
<pre class="grammar">
float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers
</pre>

The predeclared complex types are:
<pre class="grammar">
complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts
</pre>

<p>
To avoid portability issues all numeric types are <a href="#Type_definitions">defined
types</a> and thus distinct except
<code>byte</code>, which is an <a href="#Alias_declarations">alias</a> for <code>uint8</code>, and
<code>rune</code>, which is an alias for <code>int32</code>.
Explicit conversions
are required when different numeric types are mixed in an expression
or assignment. For instance, <code>int32</code> and <code>int</code>
are not the same type even though they may have the same size on a
particular architecture.
```
## Discussion

This proposal resolves the ambiguity in such a way, that the definition of the individual numeric types is as broad as possible. I guess, this is in accordance with the intention of the current wording. 

With this proposal any ambiguity about what integer, floating-point, or complex types are is resolved in a disputable, but hopefully sensible manner. As a notable example, from this adjusted definition it is now clear, whether in the language
specification indexing by integer types includes includes indexing by `int32`, too, or unsigned integer types, in contrast to the previous version, where this was not clear from the language specification itself.

Since the previous definition was ambiguous, it is disputable, whether the adaption of this proposal does any breaking changes. Since effectively some behavior of the go compiler (for example with regard to indexing) could be argued to be a feature of the compiler and not the language itself.

I suspect the current compiler as well as other tools already are conformal to the proposed change to the language specification. However, I did not test this thoroughly. The expected cost of this proposal is to review, whether 
current tools indeed accept the numeric types as defined above (and reject others) whenever this is enforced by the language specification. I suspect the cost of adapting this proposal to be low.

This proposal would make Go easier to learn. Moreover, less experienced programmers would benefit most from this proposal, as more experienced programmers are more likely to intuitively assume the interpretation of the language specification, which is now made explicit with this proposal, and most likely already fully implemented by the go tool chain.