Skip to content

proposal: Go 2: allow converting same sized ints to uints for pointers, arrays, array pointers and slices #53233

@Jorropo

Description

@Jorropo

Author background

  • Would you consider yourself a novice, intermediate, or experienced Go programmer? experienced
  • What other languages do you have experience with? python, lua, solidty, assembly

Related proposals

  • Has this idea, or one like it, been proposed before? at least couldn't find it
  • Does this affect error handling? no
  • Is this about generics? no

Proposal

  • What is the proposed change?
    With pointers, arrays, array pointers and slices, allow casting and sharing references (except for arrays) for same sized integers regardless of their sign:
var v int8 = 10

a := &v
b := (*uint8)(a)
*b = 254

fmt.Println(*a, v) // -2 -2
v := make([]uint8, 1)

b := []int8(v)
b[0] = -1

fmt.Println(b) // [255]
  • Who does this proposal help, and why?
    It allows you to skip copies when using APIs that mix ints and uints slices and array pointers.
  • Please describe as precisely as possible the change to the language.

When trying to convert pointers, arrays, array pointers and slices of integers, if the integers are of the same size then the conversion works. Even if the signs do not match.
For pointers, array pointers and slices, the convertee and converted value must share whatever they reference to.

  • What would change in the language spec? yes
  • Please also describe the change informally, as in a class teaching Go.

First make a ~5 minute course about two's complement arithmetic, how the lower part of signed numbers of space is flipped upside down and really just a mirror of the upper halfs of unsigned numbers.
Show that additions and subtractions are sign anonym operations with two's complement.
Then reach the conclusion that (except for multiplications and divisions), in two's complement the sign doesn't really exists and is just a question of how we interpret the number.

Then with the knowlege of two's complement, explain why it just make sense. Since ints and uints are really the same thing, the only thing that change it just how you read it. It's logical golang would let you cast slices of ints to slices of uints, THEY ARE THE SAME THING!

  • Is this change backward compatible? yes

The current spec says:

The value of an n-bit integer is n bits wide and represented using two's complement arithmetic.

  • Orthogonality: how does this change interact or overlap with existing features? this is technically doable already with sliceheader and unsafe tricks
  • Is the goal of this change a performance improvement? yes
    • If so, what quantifiable improvement should we expect? avoid copies on API that mix ints and uints slices and array pointers
    • How would we measure it? write a function that takes a []int and pass a reference to a noescape function taking a []uint. Right now you need two copies, with this proposal none.

Costs

  • Would this change make Go easier or harder to learn, and why? slightly harder
    it's hard to make sense of this feature if you don't understand two's complement. I don't really think this is an issue, this is clearly a really optional feature, 99% of go code wont ever use this or need to.
  • What is the cost of this proposal? (Every language change has a cost). small
  • How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected? all of them that understand the type system
  • What is the compile time cost? minimal (there is a few more checks when trying to convert pointers, array, array pointers and slices)
  • What is the run time cost? negative
  • Can you describe a possible implementation?
func getPointyTypeThroughArrayPointers(t Type) Type {
  if isPointer := t.IsPointer(); isPointer || t.IsArray() || t.Slice() {
    eType := cType.Elem()
    if isPointer && eType.IsArray() {
      eType = eType.Elem()
    }
    return eType
  }
  return nil
}

// While trying to convert things
// convertee is the SSA Value of the argument passed in
cpType := getPointyTypeThroughArrayPointers(convertee.Type())
tpType := getPointyTypeThroughArrayPointers(targetType)
if cpType != nil && tpType != nil && cpType.IsInteger() && tpType.IsInteger() && cpType.Size() == tpType.Size() {
  return b.NewValue(OpCopy, targetType, convertee)
}
  • Do you have a prototype? (This is not required.) no

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions