-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
In the following snippet, the compiler should be able to devirtualize the fi.Foo() call, but currently this does not happen (godbolt):
package main
import "fmt"
type foo struct{}
func (_ foo) Foo() { fmt.Println("Foo") }
func main() {
f := foo{}
switch fi := any(f).(type) {
case interface{ Foo() }:
fi.Foo()
}
}The example is obviously artificial, but this definitely occurs in real code: e.g. see the code generated by envoyproxy/protoc-gen-validate, e.g. https://github.com/envoyproxy/go-control-plane/blob/99c9ceb26c9d7bf1705833b7debdebe9ae67c0ae/envoy/data/tap/v2alpha/transport.pb.validate.go.
Another use case that will become increasingly important with generics in 1.18 is to distinguish1 between different types provided in type unions, e.g.:
func Foo[Bytes interface{ []byte | string }](b Bytes) string {
switch any(b).(type) {
case []byte:
return "[]byte"
case string:
return "string"
}
return "<unknown>"
}As above, this missed devirtualization opportunity currently prevents (godbolt) the compiler from statically deducing the correct branch at compile-time, and pruning the dead branches.
This is somewhat related to #38992 in that also in this specific case it would be beneficial to run inlining again after devirtualization. For example, in the first example above, inlining after devirtualization would open up significant inlining opportunities in case an embedded message does not have validation rules (in which case its Validate* functions would simply return nil, potentially allowing to prune whole branches of the validation call tree)
Footnotes
-
AFAICT this wouldn't currently be possible if the type constraint includes approximation elements (
~T); the example does not use approximation elements for this reason ↩