| // run | |
| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Test that type assertions and type switches in generic functions | |
| // produce correct results when the compiler eliminates impossible | |
| // cases based on shape type analysis. | |
| package main | |
| import "fmt" | |
| func switchStringOrBytes[S string | []byte](x S) string { | |
| switch any(x).(type) { | |
| case string: | |
| return "string" | |
| case []byte: | |
| return "[]byte" | |
| } | |
| return "unknown" | |
| } | |
| func switchThree[S string | []byte | int](x S) string { | |
| switch any(x).(type) { | |
| case string: | |
| return "string" | |
| case []byte: | |
| return "[]byte" | |
| case int: | |
| return "int" | |
| } | |
| return "unknown" | |
| } | |
| type MyString string | |
| func switchNamed[S string | MyString](x S) string { | |
| switch any(x).(type) { | |
| case string: | |
| return "string" | |
| case MyString: | |
| return "MyString" | |
| } | |
| return "unknown" | |
| } | |
| func commaOkString[S string | []byte](x S) (string, bool) { | |
| v, ok := any(x).(string) | |
| return v, ok | |
| } | |
| func commaOkBytes[S string | []byte](x S) ([]byte, bool) { | |
| v, ok := any(x).([]byte) | |
| return v, ok | |
| } | |
| func commaOkChain[S string | []byte | int](x S) string { | |
| if _, ok := any(x).(string); ok { | |
| return "string" | |
| } | |
| if _, ok := any(x).([]byte); ok { | |
| return "[]byte" | |
| } | |
| if _, ok := any(x).(int); ok { | |
| return "int" | |
| } | |
| return "unknown" | |
| } | |
| // Intermediate variable tests. | |
| func commaOkViaVar[S string | []byte](x S) (string, bool) { | |
| iface := any(x) | |
| v, ok := iface.(string) | |
| return v, ok | |
| } | |
| func switchViaVar[S string | []byte](x S) string { | |
| iface := any(x) | |
| switch iface.(type) { | |
| case string: | |
| return "string" | |
| case []byte: | |
| return "[]byte" | |
| } | |
| return "unknown" | |
| } | |
| // When no switch case matches the shape, the default is taken. | |
| func switchFallsToDefault[S string | []byte | int](x S) string { | |
| switch any(x).(type) { | |
| case string: | |
| return "string" | |
| case []byte: | |
| return "[]byte" | |
| } | |
| return "other" | |
| } | |
| func main() { | |
| check("switchStringOrBytes string", switchStringOrBytes("hello"), "string") | |
| check("switchStringOrBytes []byte", switchStringOrBytes([]byte("hello")), "[]byte") | |
| check("switchThree string", switchThree("x"), "string") | |
| check("switchThree []byte", switchThree([]byte("x")), "[]byte") | |
| check("switchThree int", switchThree(42), "int") | |
| check("switchNamed string", switchNamed("hi"), "string") | |
| check("switchNamed MyString", switchNamed(MyString("hi")), "MyString") | |
| v1, ok1 := commaOkString("hello") | |
| check("commaOkString[string] val", v1, "hello") | |
| checkBool("commaOkString[string] ok", ok1, true) | |
| v2, ok2 := commaOkString([]byte("hello")) | |
| check("commaOkString[[]byte] val", v2, "") | |
| checkBool("commaOkString[[]byte] ok", ok2, false) | |
| v3, ok3 := commaOkBytes([]byte("world")) | |
| check("commaOkBytes[[]byte] val", string(v3), "world") | |
| checkBool("commaOkBytes[[]byte] ok", ok3, true) | |
| v4, ok4 := commaOkBytes("world") | |
| check("commaOkBytes[string] val", string(v4), "") | |
| checkBool("commaOkBytes[string] ok", ok4, false) | |
| check("commaOkChain string", commaOkChain("x"), "string") | |
| check("commaOkChain []byte", commaOkChain([]byte("x")), "[]byte") | |
| check("commaOkChain int", commaOkChain(42), "int") | |
| // Intermediate variable: comma-ok | |
| v5, ok5 := commaOkViaVar("hello") | |
| check("commaOkViaVar[string] val", v5, "hello") | |
| checkBool("commaOkViaVar[string] ok", ok5, true) | |
| v6, ok6 := commaOkViaVar([]byte("hello")) | |
| check("commaOkViaVar[[]byte] val", v6, "") | |
| checkBool("commaOkViaVar[[]byte] ok", ok6, false) | |
| // Intermediate variable: type switch | |
| check("switchViaVar string", switchViaVar("x"), "string") | |
| check("switchViaVar []byte", switchViaVar([]byte("x")), "[]byte") | |
| // All cases impossible: int instantiation hits default | |
| check("switchFallsToDefault string", switchFallsToDefault("x"), "string") | |
| check("switchFallsToDefault []byte", switchFallsToDefault([]byte("x")), "[]byte") | |
| check("switchFallsToDefault int", switchFallsToDefault(42), "other") | |
| } | |
| func check(name, got, want string) { | |
| if got != want { | |
| panic(fmt.Sprintf("%s: got %q, want %q", name, got, want)) | |
| } | |
| } | |
| func checkBool(name string, got, want bool) { | |
| if got != want { | |
| panic(fmt.Sprintf("%s: got %v, want %v", name, got, want)) | |
| } | |
| } |