Depending on which implementation you use, the following may produce the same instructions as the example above. Go on, try it!
package main
import "fmt"
func foo(x *X) int {
return x.y
}
type X struct {
y int
}
func main() {
xx := X{3}
fmt.Println(foo(&xx))
}
Now, gc will give you two different sets of instructions from these two different programs. I expect that is what you are really trying and failing to say, but that is not something about Go. Go allows devirualizing and monomorphizing of the former program just fine. An implementation may choose not to, but the same can be said for C++. Correct me if I'm wrong, but from what I recall devirtualization/monomorphization is not a requirement of C++ any more than it is of Go. It is left to the discretion of the implementer.
Tried it out in godbolt, yes you are right that with the above example gc is able to realize that
func foo[V any, X Yer[V]](x X) V
can be called with only one type (X) and therefore manages to emit the same code in main_main_pc0. It all falls apart when you add a second struct which satisfies Yer [1], which leads the compiler to emit a virtual function table instead. You can see it in the following instructions in the code with a second implementation for Yer added:
Was it really necessary to try in gc...? We already talked about how it produces different instructions for the different programs. Nice of you to validate what I already said, I suppose, but this doesn't tell any of us anything we didn't already know.
The intent was for you to try it in other implementations to see how they optimize the code.