Axel Wagner, I've "read" the thread you linked (read some posts and skimmed through others) and also read the following article (which I should've read sooner):
and now I understand the issue a little bit better (hopefully). As a result I have the only suggestion left for now in regards to generic methods:
1. For an interface's generic methods disallow the use of "any" constraint for type parameters.
type HasIdentity interface {
Identity[T int64 | float64](T) T // ok
// Identifoo[T any](T) T // not allowed
}
2. For a type's generic methods there should be no restrictions for constraints.
type Foo struct {...}
func (foo Foo) Identity[T any](v T) T { return v } // ok
3. Generic methods that are invoked on variables of concrete types should behave just like regular generic functions.
foo := Foo{}
v := foo.Identity(123) // the same as calling Identity[T any](foo Foo, v T) T with T = int and the same body as the method
4. Types' generic methods should only be able to satisfy interfaces' generic methods. Types' non-generic methods should only be able to satisfy interfaces' non-generic methods.
5. When the whole program code is analyzed by the compiler we should know exactly which types satisfy which interfaces and which types satisfy which constraints. So...
5a. Every generic method in every interface should be turned into several non-generic methods with their type parameters substituted for every possible concrete types according to their constraints. For the HasIdentity example above we'll have:
type HasIdentity interface {
Identity[int64](int64) int64
Identity[float64](float64) float64
}
5b. For every type that has generic methods and that satisfies some interface the corresponding constraints from the interfaces' generic method type parameters should be used to generate all possible method variations for this type. So for the Foo struct above we know that according to its generic Identity method signature it should satisfy the interface HasIdentity so we take the corresponding type parameter constraint from HasIdentity's method (int64 | float64) and use it to generate the needed methods:
type Foo struct{}
func (foo Foo) Identity[int64](v int64) int64 { return v }
func (foo Foo) Identity[float64](v float64) float64 { return v }
So the usage can be:
var a any = Foo{}
// the HasIdentity method table has 2 entries
// which map to the corresponding 2 entries in the Foo method table
h := a.(HasIdentity)
i := h.Identity(123)
f := h.Identity(1.0)
Hopefully I conveyed the main idea. Looking forward to a feedback.