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

spec: un-conflate variables and values #20181

Open
alercah opened this issue Apr 30, 2017 · 4 comments
Open

spec: un-conflate variables and values #20181

alercah opened this issue Apr 30, 2017 · 4 comments

Comments

@alercah
Copy link

alercah commented Apr 30, 2017

The notions of variable and value, although meaningfully distinct, are often conflated in the spec. Some examples:

  1. Dynamic types are defined as a property of interface variables, but are properly a property of interface values. The definition of static type also treats elements of structs, arrays, and slices as variables (see next point).
  2. There is a definition of structured type that provides that elements of slices, arrays, and structs and like variables. Putting aside the question of whether slices have elements or not, this is not true in any case of unaddressable arrays.
  3. Pointers are defined as pointing to variables, not addressable values.
  4. Variables of interface type are capable of storing values of any compatible type, but no mention is made of interface values.
  5. "In other words, the switch expression is treated as if it were used to declare and initialize a temporary variable t without explicit type; it is that value of t against which each case expression x is tested for equality." does not seem like it actually clarifies anything to me; rather, it is just gymnastics to work around the possibility that values don't have a properly defined existence.

This is probably all of them, but I'm not sure.

@alercah
Copy link
Author

alercah commented Apr 30, 2017

Found one more: the definition of pointer equality says that things point to the same variable, but as currently defined, slice elements count as variable. Thus, arguably, a := [1]int{1}; &a[0] == &(a[:])[0] could be false because the slice element is a distinct variable from the array element.

It's of note that formally defining this to always be true provides a defined way to compare slices of non-zero-size element type.

@mdempsky
Copy link
Member

mdempsky commented Apr 30, 2017

Dynamic types are defined as a property of interface variables, but are properly a property of interface values.

Agreed this is an issue: dynamic type should be a property of interface values, not interface variables.

The definition of static type also treats elements of structs, arrays, and slices as variables (see next point).
There is a definition of structured type that provides that elements of slices, arrays, and structs and like variables. Putting aside the question of whether slices have elements or not, this is not true in any case of unaddressable arrays.

Not sure I follow the issue. Is the problem that we say "Structured variables of array, slice, and struct types have elements" when zero-length arrays and empty structs do not?

Pointers are defined as pointing to variables, not addressable values.

I believe this is correct. Values are not addressable, but when they're stored in memory (which is always termed a "variable") they are.

Variables of interface type are capable of storing values of any compatible type, but no mention is made of interface values.

Agreed this seems to be worth clarifying (unless I'm overlooking something). We define that the dynamic type must be a "concrete" type, which I believe is intended to mean a non-interface type, but we don't actually define that.

I at least think "A variable of interface type can store a value of any type with a method set that is any superset of the interface." needs to be clarified to something like "A value of interface type can store a value of any concrete type with ...".

does not seem like it actually clarifies anything to me

I believe it's meant to explain that

switch e {
case c1, c2: ...
case c3, c4: ...
}

means the same as:

if t := e; t == c1 || t == c2 {
    ...
} else if t == c3 || t == c4 {
    ...
}

See 2d9378c for the ambiguities this wording was introduced to address.

Found one more: the definition of pointer equality says that things point to the same variable, but as currently defined, slice elements count as variable. Thus, arguably, a := [1]int{1}; &a[0] == &(a[:])[0] could be false because the slice element is a distinct variable from the array element.

Slice types are defined to reference the storage of an underlying array:

A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array

And slice expressions are defined to access these:

Otherwise, the result shares its underlying array with the operand.

Because &a[0] and &a[:][0] always refer to the same storage location (i.e., variable), they're equal pointer values (assuming len(a) > 0).

/cc @griesemer @ianlancetaylor

@bradfitz bradfitz added this to the Unplanned milestone Apr 30, 2017
@alercah
Copy link
Author

alercah commented Apr 30, 2017

Not sure I follow the issue. Is the problem that we say "Structured variables of array, slice, and struct types have elements" when zero-length arrays and empty structs do not?

No, the issue is that this definition says that elements of arrays are treated like variables. But this is not true of unaddressable arrays. The same goes for elements of unaddressable structs.

I believe this is correct. Values are not addressable, but when they're stored in memory (which is always termed a "variable") they are.

The spec defines an addressable value as "either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array." and refers to this on several occasions. I think this is a much better approach then trying to refer to them as variables, because it is more comprehensive. The "act like variables" language when discussing structured variables is unclear whether that is normative text saying "they count as variables for the purposes of the spec" or merely informative implying "they are addressable and thus can be used in many of the same contexts as variables". A cleaner way to write this, perhaps, is to say that an addressable value has an address, and then say that two addressable values have the same address if (not counting zero-size objects):

  • They are both the same variable; or
  • They are both the same element of an array or struct with the same address.

If an operand is the address of a pointer indirection operation, then its address is the address stored in the pointer. If an operand is a slice index operation, its address is the address of the corresponding element of the underlying array.

I believe it's meant to explain that...

Ah, thanks. Sounds good.

Slice types are defined to reference the storage of an underlying array:

And slice expressions are defined to access these:

Because &a[0] and &a[:][0] always refer to the same storage location (i.e., variable), they're equal pointer values (assuming len(a) > 0).

I agree in all but the definition of the space. The issue with spec as written is that defines pointer equality as being equality of variables:

Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.

The spec also says, in the text I quoted earlier about structured types, that slice elements and array elements are variables. It is unclear at best about whether or not the identity of *a[0] as a variable is the same as &a[:][0] as a variable. I think the argument that they are the same is fairly strong, perhaps based on:

A slice... provides access to a numbered sequence of elements from that array.

However, the way this works is, in my opinion, needlessly complicated by the lack of an explicit statement.

@griesemer griesemer self-assigned this Apr 30, 2017
@mdempsky
Copy link
Member

No, the issue is that this definition says that elements of arrays are treated like variables.

Okay, that's intentional though and the convention chosen for the Go spec: "variable" is the term used to refer to any memory storage location where a value can be stored and that can be addressed. Variables can contain other variables. Some variables are explicitly declared with var declarations, but many others are implicitly allocated and do not have names (e.g., the new builtin or taking the address of a composite literal both allocate anonymous variables).

But this is not true of unaddressable arrays. The same goes for elements of unaddressable structs.

The elements of array and struct values (as opposed to variables) are not addressable, so they're not variables.

The spec defines an addressable value as "either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array." and refers to this on several occasions.

I see. Perhaps we should rephrase that to "addressable expression". In all cases, the expression identifies a variable / memory location.

@ALTree ALTree changed the title spec: un-conflate variables and values. spec: un-conflate variables and values Jul 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants