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: assignment rules need clarification when type parameters are involved #52628

Closed
bcmills opened this issue Apr 30, 2022 · 3 comments
Closed
Labels
Documentation FrozenDueToAge generics Issue is related to generics NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@bcmills
Copy link
Contributor

bcmills commented Apr 30, 2022

The current spec says:

For a type parameter that is the underlying type of its type constraint, which is always an interface.

And (emphasis mine):

A non-constant value x can be converted to type T in any of these cases:

• ignoring struct tags (see below), x's type and T are not type parameters but have identical underlying types.

And:

A type definition creates a new, distinct type with the same underlying type and operations as the given type and binds an identifier, the type name, to it.

In a type definition the given type cannot be a type parameter.

And:

For unification, two types that don't contain any type parameters from the current type parameter list are equivalent if they are identical, or if they are channel types that are identical ignoring channel direction, or if their underlying types are equivalent.

But, even worse:

A value x of type V is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:

• V and T have identical underlying types and at least one of V or T is not a named type.

If that last one actually held, then this program would be valid, but it clearly cannot be!
(https://go.dev/play/p/ofKOhQpJxgq)

func f[T interface{}]() {
	var x interface{} = 42

	// If the underlying type of T “is its type constraint”,
	// then here the underlying type of T is interface{}.
	//
	// The type interface{} is a type literal, and
	// “[i]f T is … a type literal, the corresponding underlying type is T itself.”
	// So the underlying type of x is also interface{}.
	//
	// According to the spec, “[a] value x of type V is assignable
	// to a variable of type T ("x is assignable to T") if …
	// V and T have identical underlying types
	// and at least one of V or T is not a named type.”
	//
	// Here, V and T both have underlying type interface{},
	// and V is a type literal (not a named type).
	// So x ought to be assignable to type T.
	// But clearly it cannot be, because T could
	// be any type at all, including "string" or "bool"!

	var a T = x
}

So we see that nearly everywhere that the concept of “underlying types” is used, type parameters are either excluded entirely or treated differently — except for one place, where the fact that they aren't treated differently is an error in the spec.


That suggests to me that the definition of “underlying type” for type parameters is wrong: it doesn't provide any value in the actual uses of the concept of “underlying type” (since they all end up needing special cases to avoid it), and doesn't add consistency in and of itself.

(CC @griesemer, @ianlancetaylor, @mdempsky, @findleyr)

@bcmills
Copy link
Contributor Author

bcmills commented Apr 30, 2022

In #45346 (comment), I suggested an alternative:

As far as I can tell, the underlying type of a type parameter P should itself be an (anonymous) type parameter.
The anonymous parameter … is constrained to the union of the underlying types in the type-set of P.

I still believe that that alternative would be more coherent than what is in the spec today.
However, it would also require that interface types be included in type-sets, since the underlying type of a type parameter instantiated with an interface type literal really is the interface type itself.

@bcmills bcmills added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Apr 30, 2022
@bcmills bcmills added this to the Go1.19 milestone Apr 30, 2022
@bcmills bcmills added the generics Issue is related to generics label Apr 30, 2022
@griesemer
Copy link
Contributor

@bcmills The spec has special rules for assignability when type parameters are involved. The section on assignability says explicitly:

Additionally, if x's type V or T are type parameters, x is assignable to a variable of type T if one of the following conditions applies: ...

This section explicitly forbids the program you are mentioning and the implementation does the right thing.

It may be that the section beforehand needs clearer prose to exclude this case which is handled later. But that's not the same as calling it an inconsistency. Retitled.

With respect to whether the underlying type of a type parameter is its constraint interface: note that it is an interface with a "fixed" dynamic type, leading to separate rules. For instance, we cannot assign to them as if they were normal interfaces for exactly the reason you have pointed out above. But we can call their methods. We could type assert (if we were going to allow that), etc. There are fewer caveats ("special cases" as you call them) this way then if we define the underlying type of type parameters differently and then we always have to somehow refer to the type constraint of the type parameter to explain the functionality of the type parameter. As written, we can simply refer to a type parameter's type set (which is the type set of it's interface), the same way we refer to say the "element of slice S" rather than the "element of the underlying type of "S". In short, the current definition of the underlying type of a type parameter does add quite a bit of value, at least when it comes to the prose in the spec. It also explains various type parameter behavior for which we otherwise would have to invent some (possibly inconsistent) rules.

@griesemer griesemer changed the title spec: “underlying type” for type parameters is inconsistent spec: assignment rules may need clarification when type parameters are involved Apr 30, 2022
@griesemer griesemer self-assigned this Apr 30, 2022
@griesemer griesemer changed the title spec: assignment rules may need clarification when type parameters are involved spec: assignment rules need clarification when type parameters are involved May 11, 2022
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/405755 mentions this issue: spec: correct assignment rules with respect to type parameters

@dmitshur dmitshur added NeedsFix The path to resolution is known, but the work has not been done. Documentation and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels May 22, 2022
@golang golang locked and limited conversation to collaborators Jun 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Documentation FrozenDueToAge generics Issue is related to generics NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

4 participants