Source file test/typeparam/shape_assert.go

     1  // run
     2  
     3  // Copyright 2026 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // Test that type assertions and type switches in generic functions
     8  // produce correct results when the compiler eliminates impossible
     9  // cases based on shape type analysis.
    10  
    11  package main
    12  
    13  import "fmt"
    14  
    15  func switchStringOrBytes[S string | []byte](x S) string {
    16  	switch any(x).(type) {
    17  	case string:
    18  		return "string"
    19  	case []byte:
    20  		return "[]byte"
    21  	}
    22  	return "unknown"
    23  }
    24  
    25  func switchThree[S string | []byte | int](x S) string {
    26  	switch any(x).(type) {
    27  	case string:
    28  		return "string"
    29  	case []byte:
    30  		return "[]byte"
    31  	case int:
    32  		return "int"
    33  	}
    34  	return "unknown"
    35  }
    36  
    37  type MyString string
    38  
    39  func switchNamed[S string | MyString](x S) string {
    40  	switch any(x).(type) {
    41  	case string:
    42  		return "string"
    43  	case MyString:
    44  		return "MyString"
    45  	}
    46  	return "unknown"
    47  }
    48  
    49  func commaOkString[S string | []byte](x S) (string, bool) {
    50  	v, ok := any(x).(string)
    51  	return v, ok
    52  }
    53  
    54  func commaOkBytes[S string | []byte](x S) ([]byte, bool) {
    55  	v, ok := any(x).([]byte)
    56  	return v, ok
    57  }
    58  
    59  func commaOkChain[S string | []byte | int](x S) string {
    60  	if _, ok := any(x).(string); ok {
    61  		return "string"
    62  	}
    63  	if _, ok := any(x).([]byte); ok {
    64  		return "[]byte"
    65  	}
    66  	if _, ok := any(x).(int); ok {
    67  		return "int"
    68  	}
    69  	return "unknown"
    70  }
    71  
    72  // Intermediate variable tests.
    73  func commaOkViaVar[S string | []byte](x S) (string, bool) {
    74  	iface := any(x)
    75  	v, ok := iface.(string)
    76  	return v, ok
    77  }
    78  
    79  func switchViaVar[S string | []byte](x S) string {
    80  	iface := any(x)
    81  	switch iface.(type) {
    82  	case string:
    83  		return "string"
    84  	case []byte:
    85  		return "[]byte"
    86  	}
    87  	return "unknown"
    88  }
    89  
    90  // When no switch case matches the shape, the default is taken.
    91  func switchFallsToDefault[S string | []byte | int](x S) string {
    92  	switch any(x).(type) {
    93  	case string:
    94  		return "string"
    95  	case []byte:
    96  		return "[]byte"
    97  	}
    98  	return "other"
    99  }
   100  
   101  func main() {
   102  	check("switchStringOrBytes string", switchStringOrBytes("hello"), "string")
   103  	check("switchStringOrBytes []byte", switchStringOrBytes([]byte("hello")), "[]byte")
   104  
   105  	check("switchThree string", switchThree("x"), "string")
   106  	check("switchThree []byte", switchThree([]byte("x")), "[]byte")
   107  	check("switchThree int", switchThree(42), "int")
   108  
   109  	check("switchNamed string", switchNamed("hi"), "string")
   110  	check("switchNamed MyString", switchNamed(MyString("hi")), "MyString")
   111  
   112  	v1, ok1 := commaOkString("hello")
   113  	check("commaOkString[string] val", v1, "hello")
   114  	checkBool("commaOkString[string] ok", ok1, true)
   115  	v2, ok2 := commaOkString([]byte("hello"))
   116  	check("commaOkString[[]byte] val", v2, "")
   117  	checkBool("commaOkString[[]byte] ok", ok2, false)
   118  
   119  	v3, ok3 := commaOkBytes([]byte("world"))
   120  	check("commaOkBytes[[]byte] val", string(v3), "world")
   121  	checkBool("commaOkBytes[[]byte] ok", ok3, true)
   122  	v4, ok4 := commaOkBytes("world")
   123  	check("commaOkBytes[string] val", string(v4), "")
   124  	checkBool("commaOkBytes[string] ok", ok4, false)
   125  
   126  	check("commaOkChain string", commaOkChain("x"), "string")
   127  	check("commaOkChain []byte", commaOkChain([]byte("x")), "[]byte")
   128  	check("commaOkChain int", commaOkChain(42), "int")
   129  
   130  	// Intermediate variable: comma-ok
   131  	v5, ok5 := commaOkViaVar("hello")
   132  	check("commaOkViaVar[string] val", v5, "hello")
   133  	checkBool("commaOkViaVar[string] ok", ok5, true)
   134  	v6, ok6 := commaOkViaVar([]byte("hello"))
   135  	check("commaOkViaVar[[]byte] val", v6, "")
   136  	checkBool("commaOkViaVar[[]byte] ok", ok6, false)
   137  
   138  	// Intermediate variable: type switch
   139  	check("switchViaVar string", switchViaVar("x"), "string")
   140  	check("switchViaVar []byte", switchViaVar([]byte("x")), "[]byte")
   141  
   142  	// All cases impossible: int instantiation hits default
   143  	check("switchFallsToDefault string", switchFallsToDefault("x"), "string")
   144  	check("switchFallsToDefault []byte", switchFallsToDefault([]byte("x")), "[]byte")
   145  	check("switchFallsToDefault int", switchFallsToDefault(42), "other")
   146  }
   147  
   148  func check(name, got, want string) {
   149  	if got != want {
   150  		panic(fmt.Sprintf("%s: got %q, want %q", name, got, want))
   151  	}
   152  }
   153  
   154  func checkBool(name string, got, want bool) {
   155  	if got != want {
   156  		panic(fmt.Sprintf("%s: got %v, want %v", name, got, want))
   157  	}
   158  }
   159  

View as plain text