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

cmd/go: awkward composite literal edge case #45396

Closed
rogpeppe opened this issue Apr 5, 2021 · 7 comments
Closed

cmd/go: awkward composite literal edge case #45396

rogpeppe opened this issue Apr 5, 2021 · 7 comments
Labels
FrozenDueToAge generics Issue is related to generics NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@rogpeppe
Copy link
Contributor

rogpeppe commented Apr 5, 2021

In this comment I brought up an edge case for type parameters:

type C interface {
	map[int]string | []string | [2]string | struct{b1, b2 string}
}

const b1, b2 = 0, 1

func f[A C]() A {
	return A{
		b1: "a",
		b2: "b",
	}
}

Should this be valid or not?
Note that if C contains only one of any of the type list above, the code works for that type.

[Edited to bring syntax up to date]

@ianlancetaylor
Copy link
Contributor

CC @griesemer

@griesemer griesemer added this to the Go1.18 milestone Apr 5, 2021
@griesemer griesemer added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Apr 5, 2021
@griesemer
Copy link
Contributor

My inclination would be that this shouldn't be valid. More generally, we need to pin down exactly what the currently proposed rules mean when they say "all operations supported by all types in a type set" will be permitted.

@apparentlymart
Copy link

apparentlymart commented Apr 9, 2021

FWIW as just a user without any particular investment in any specific answer to this question, my first intuition told me that this would not be valid because I see four different semantic "operations" here, even though all of them have the same syntax:

  • Construct a map value
  • Construct a slice value (and its underlying array)
  • Construct an array value
  • Construct a value of this particular struct type

However, this did get me thinking about other similar situations where the syntax could be said to overlap even though the semantics might not:

type C interface {
	type map[int]string, []string, chan string. []int, chan int
}

func f[A C]() A {
	return make(A)
}

make is defined in the specification as a "built in function", which suggests to me that there is only one "thing" represented by that keyword make, and that thing happens to take a type as a first argument. In which case it would stand to reason that it could accept any type parameter A which is provably a slice, map, or channel type.

Which then led me back to ambivalence, because I'm not sure it feels justified to me for make to have different rules than a composite literal just because one of them happens to be a predeclared function while the other has a specialized grammar associated with it; they both have comparable purposes in the language, and both are defined as a singular "thing" in the spec which happens to take a type as an argument.

Having written this out I find myself doubting whether I've actually added anything to the discussion, because I left feeling far less convinced than I started! But I hope at least the analogy to make is a useful example to consider while thinking about this.

@griesemer
Copy link
Contributor

@apparentlymart I think at the end of the day this is the kind of thing we need to explicitly write down in the spec. make(A) is an "operation" applied to an operand, similar to x + 1 where + is an operation applied to x where x might be of a type parameter type. But the struct composite literal

A{
	b1: "a",
	b2: "b",
}

where b1 and b2 are actual struct field names is something different (and different from the ones where the b1 and b2 are key values). We probably don't want to allow this.

@kfsone
Copy link

kfsone commented Apr 21, 2021

I want everyone to LOOK, A SQUIRREL before someone else comes in to suggest lua-style [b1] syntax or something equally heinous.

I do think this is something that will need a solution but I think that the introduction of generics can survive the inability to memberwise-construct compounds: so I propose completely disallowing it in the initial launch of generics.

func f[A C]() A {
  a := A{ x: 1 }

might produce the diagnostic

error: generic functions may not construct a compound literal with named fields. see ...link...

Better to have this omission than to reinvent c++ templates.

@griesemer
Copy link
Contributor

This will not be in the Go1.18 release. Leaving open for future discussion.

@griesemer griesemer modified the milestones: Go1.18, Go1.19 Sep 24, 2021
@rogpeppe rogpeppe changed the title cmd/go2go: awkward composite literal edge case cmd/go: awkward composite literal edge case Nov 26, 2021
@rogpeppe rogpeppe added the generics Issue is related to generics label Nov 26, 2021
@griesemer
Copy link
Contributor

The 1.18 spec now explicitly requires a core type for composite literals. I am going to close this issue as decided for the time being.

If this is important to change, we need an actual proposal that outlines exactly what should be permitted and what should not be permitted in this case. I'm particularly concerned about mixing index values and struct field names, and to a lesser extent, mixing map and non-map types. These two cases will need to be explored in detail in a proposal.

@golang golang locked and limited conversation to collaborators Jun 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge generics Issue is related to generics NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

6 participants