Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: spec: allow slice-to-array conversion expressions to present as L-values. #47987

Closed
go101 opened this issue Aug 26, 2021 · 17 comments
Closed

Comments

@go101
Copy link

go101 commented Aug 26, 2021

This proposal allow conversion from slice to array has already been accepted. But whether or not such conversion could be used as L-values is not decided along together. @ianlancetaylor suggested this undecided problem should be discussed in a new issue thread, so I created this one.

After the "allow conversion from slice to array" proposal is accepted, the following code will become valid in Go 1.18:

var s = make([]T, n)
var a = [N]T(s) // The type of a is {N}T. If N > n, a panic will occur.

By my understanding, the semantics of the conversion [N]T(s) means viewing the first N elements (of type T) in s as an array (of type [N]T).

The elements of a slice are always addressable and could be modified, even if the slice itself is not addressable. For example:

	[]int{1, 2, 3}[2] = 9    // compiles okay
	_ = &([]int{1, 2, 3}[1]) // compiles okay

So the conversion results of [N]T(s) should be also always an addressable array, which means it could be used as L-values, a.k.a. it could be used as the destination values in value assignments. An example:

var s = make([]T, 10000)
[3]T(s[500:]) = [3]T{500, 501, 502} // compiles okay by this proposal

The above is the rationale of this proposal.

@gopherbot gopherbot added this to the Proposal milestone Aug 26, 2021
@bcmills
Copy link
Contributor

bcmills commented Aug 27, 2021

Converting a slice to an array results in a new, distinct array value (https://play.golang.org/p/9gaPIPkAe1I). So in

	[3]T(s[500:]) = [3]T{500, 501, 502}

the store would be to a newly-allocated [3]T which is then immediately garbage-collected.

@bcmills
Copy link
Contributor

bcmills commented Aug 27, 2021

In contrast, the equivalent non-dead store (converting to a pointer-to-array rather than an array) works today (https://play.golang.org/p/jSgzz1SspWM):

	*(*[3]T)(s[500:]) = [3]T{500, 501, 502}

@go101
Copy link
Author

go101 commented Aug 28, 2021

@bcmills I reposted your code on play site here:

package main

import "fmt"

type T int

func main() {
	var s = make([]T, 10000)
	var a [3]T = *(*[3]T)(s[500:])

	fmt.Println(&a[0], &s[500])
}

Converting a slice to an array results in a new, distinct array value

It is depended on how the semantics is defined.

In the above code, after the array a is declared, a memory block has already been allocated for it.
So in the assignment, the 3 elements in s are copied directly to a.
No duplication is made for intermediate conversion results.

@go101
Copy link
Author

go101 commented Aug 28, 2021

I think my interpretation of the semantics is more consistent with the current pointer dereference: *aPointer doesn't duplicate, assignments do. So similarly, [N]T(s) doesn't duplicate, assignments do.

package main

import "fmt"

func main() {
	var a = [3]int{1, 2, 3}
	var p = &a
	(*p)[2] = 9
	fmt.Println(a) // 1 2 9
}

@dominikh
Copy link
Member

Your interpretation is only more consistent with an incorrect understanding of Go's value semantics and conversion operations. Your proposal makes as much sense as this:

var x uint64 = 1
uint32(x) = 2

@go101
Copy link
Author

go101 commented Aug 28, 2021

@dominikh they are two different conversions. The comparison is not fair.

@go101
Copy link
Author

go101 commented Aug 28, 2021

Again, this depends on how the slice-2-array conversion semantics is defined.

Currently the following code is valid:

package main

func main() {
	var s = []int{1, 2, 3}
	(*(*[1]int)(s))[0] = 9
	println(s[0]) // 9
}

If the conversion semantics is simply defined as a shortened form of (*(*[N]T(aSlice))), the following code should be also valid.

package main

func main() {
	var s = []int{1, 2, 3}
	([1]int(s))[0] = 9
	println(s[0]) // 9
}

@dominikh
Copy link
Member

To me, the matter is rather simple: arrays are value-typed, conversions to arrays copy, and conversions to types with value semantics shouldn't be valid on the lhs. Whether conversions should ever be valid lhs is a different matter; currently they are not, which is another way in which your proposal is not consistent with the current language.

Your "shortened form" interpretation is not just a "shortened form", it is akin to a macro, requiring the user (and the compiler, as far as semantics are concerned) to do string substitution in their mind to make sense of that assignment.

Your proposal seems only consistent given the macro interpretation. Under any other interpretation, it is inconsistent with the existing language, the way conversions work, and the way value types work. You are proposing the addition of new behavior to the language; your proposal does not improve consistency with the existing semantics.

@go101
Copy link
Author

go101 commented Aug 28, 2021

As I have mentioned above. The new conversion might be different to the old ones. So the old rules might not work here.
In the first step, we need to clarify the semantics of the new conversion.

Off-topic, there are already some semantics inconsistencies in Go:

  • missing optional ok result might cause panics or not
  • composite literals can be taken addresses
  • the second arguments of copy and append calls may be strings if the first ones are byte slices

@bcmills
Copy link
Contributor

bcmills commented Aug 30, 2021

The new conversion might be different to the old ones. So the old rules might not work here.

I think that is a red flag in and of itself. The new conversion should not be different from the old ones, because that would be an inconsistency in the language — and thus a source of complexity when reasoning about Go programs, for both end users and tool authors.

@go101
Copy link
Author

go101 commented Sep 1, 2021

Will the new conversion in the following code duplicate?

package main

func main() {
	var a [3]int
	var s = make([]int, 9)
	if a == [3]int(s[2:]) {
		println("equal")
	}
}

@bcmills
Copy link
Contributor

bcmills commented Sep 1, 2021

Semantically, yes. But, as an optimization, the compiler may elide the copy, since that program can't actually detect whether a copy was made in memory.

(This is analogous to the optimization that the compiler already does for many conversions from string to []byte, which otherwise do require that the data be copied: https://play.golang.org/p/d3nW_FkN7rI.)

@ianlancetaylor ianlancetaylor added this to Incoming in Proposals (old) Sep 8, 2021
@mdempsky
Copy link
Member

mdempsky commented Sep 9, 2021

I agree with @bcmills and @dominikh. I don't think this proposal is consistent with Go semantics. Only variables are assignable, whereas conversions yield merely a value.

The case can be made that this is a particular special case of conversions where an assignment can be reasonably defined. But that would make the language more irregular, not more regular.

In my opinion, this is comparable to suggesting that we allow the assignment x + 0 = y as equivalent to x = y, because of the algebraic equivalence between x + 0 and x. Both of these expressions yield the same value, but only one of the syntactic forms is defined as "assignable" and thus valid for use on the LHS of an assignment.

@go101
Copy link
Author

go101 commented Sep 10, 2021

This proposal is more a suggestion to a new possibility.
In fact, I don't also very lean toward supporting it.

However, the comparison (and the above @dominikh's one) is not very appropriate. In fact, the conversion is more like the following one (though this one is not legal now):

var x = new(int)
int(x) = 2

@rsc rsc changed the title proposal: spec: allow slice-2-array conversion expressions to present as L-values. proposal: spec: allow slice-to-array conversion expressions to present as L-values. Oct 13, 2021
@rsc
Copy link
Contributor

rsc commented Oct 13, 2021

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@rsc rsc moved this from Incoming to Active in Proposals (old) Oct 13, 2021
@rsc rsc moved this from Active to Likely Decline in Proposals (old) Oct 20, 2021
@rsc
Copy link
Contributor

rsc commented Oct 20, 2021

Based on the discussion above, this proposal seems like a likely decline.
— rsc for the proposal review group

@rsc rsc moved this from Likely Decline to Declined in Proposals (old) Oct 27, 2021
@rsc
Copy link
Contributor

rsc commented Oct 27, 2021

No change in consensus, so declined.
— rsc for the proposal review group

@rsc rsc closed this as completed Oct 27, 2021
@golang golang locked and limited conversation to collaborators Oct 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

No branches or pull requests

6 participants