-- go.mod --
module example.com
go 1.22

-- switches.go --
package main

// This file is the input to TestSwitches in switch_test.go.
// Each multiway conditional with constant or type cases (Switch)
// discovered by Switches is printed, and compared with the
// comments.
//
// The body of each case is printed as the value of its first
// instruction.

// -------- Value switches --------

func SimpleSwitch(x, y int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: print(23:int)
	// case 3:int: print(23:int)
	// case 4:int: print(3:int)
	// default: x == y
	// }
	switch x {
	case 1:
		print(1)
	case 2, 3:
		print(23)
		fallthrough
	case 4:
		print(3)
	default:
		print(4)
	case y:
		print(5)
	}
	print(6)
}

func four() int { return 4 }

// A non-constant case makes a switch "impure", but its pure
// cases form two separate switches.
func SwitchWithNonConstantCase(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: print(23:int)
	// case 3:int: print(23:int)
	// default: four()
	// }

	// switch x {
	// case 5:int: print(5:int)
	// case 6:int: print(6:int)
	// default: print("done":string)
	// }
	switch x {
	case 1:
		print(1)
	case 2, 3:
		print(23)
	case four():
		print(3)
	case 5:
		print(5)
	case 6:
		print(6)
	}
	print("done")
}

// Switches may be found even where the source
// program doesn't have a switch statement.

func ImplicitSwitches(x, y int) {
	// switch x {
	// case 1:int: print(12:int)
	// case 2:int: print(12:int)
	// default: x < 5:int
	// }
	if x == 1 || 2 == x || x < 5 {
		print(12)
	}

	// switch x {
	// case 3:int: print(34:int)
	// case 4:int: print(34:int)
	// default: x == y
	// }
	if x == 3 || 4 == x || x == y {
		print(34)
	}

	// Not a switch: no consistent variable.
	if x == 5 || y == 6 {
		print(56)
	}

	// Not a switch: only one constant comparison.
	if x == 7 || x == y {
		print(78)
	}
}

func IfElseBasedSwitch(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: print(2:int)
	// default: print("else":string)
	// }
	if x == 1 {
		print(1)
	} else if x == 2 {
		print(2)
	} else {
		print("else")
	}
}

func GotoBasedSwitch(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: print(2:int)
	// default: print("else":string)
	// }
	if x == 1 {
		goto L1
	}
	if x == 2 {
		goto L2
	}
	print("else")
L1:
	print(1)
	goto end
L2:
	print(2)
end:
}

func SwitchInAForLoop(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: print(2:int)
	// default: print("head":string)
	// }
loop:
	for {
		print("head")
		switch x {
		case 1:
			print(1)
			break loop
		case 2:
			print(2)
			break loop
		}
	}
}

// This case is a switch in a for-loop, both constructed using goto.
// As before, the default case points back to the block containing the
// switch, but that's ok.
func SwitchInAForLoopUsingGoto(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: print(2:int)
	// default: print("head":string)
	// }
loop:
	print("head")
	if x == 1 {
		goto L1
	}
	if x == 2 {
		goto L2
	}
	goto loop
L1:
	print(1)
	goto end
L2:
	print(2)
end:
}

func UnstructuredSwitchInAForLoop(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 2:int: x == 1:int
	// default: print("end":string)
	// }
	for {
		if x == 1 {
			print(1)
			return
		}
		if x == 2 {
			continue
		}
		break
	}
	print("end")
}

func CaseWithMultiplePreds(x int) {
	for {
		if x == 1 {
			print(1)
			return
		}
	loop:
		// This block has multiple predecessors,
		// so can't be treated as a switch case.
		if x == 2 {
			goto loop
		}
		break
	}
	print("end")
}

func DuplicateConstantsAreNotEliminated(x int) {
	// switch x {
	// case 1:int: print(1:int)
	// case 1:int: print("1a":string)
	// case 2:int: print(2:int)
	// default: return
	// }
	if x == 1 {
		print(1)
	} else if x == 1 { // duplicate => unreachable
		print("1a")
	} else if x == 2 {
		print(2)
	}
}

// Interface values (created by comparisons) are not constants,
// so ConstSwitch.X is never of interface type.
func MakeInterfaceIsNotAConstant(x interface{}) {
	if x == "foo" {
		print("foo")
	} else if x == 1 {
		print(1)
	}
}

func ZeroInitializedVarsAreConstants(x int) {
	// switch x {
	// case 0:int: print(1:int)
	// case 2:int: print(2:int)
	// default: print("end":string)
	// }
	var zero int // SSA construction replaces zero with 0
	if x == zero {
		print(1)
	} else if x == 2 {
		print(2)
	}
	print("end")
}

// -------- Select --------

// NB, potentially fragile reliance on register number.
func SelectDesugarsToSwitch(ch chan int) {
	// switch t1 {
	// case 0:int: extract t0 #2
	// case 1:int: println(0:int)
	// case 2:int: println(1:int)
	// default: println("default":string)
	// }
	select {
	case x := <-ch:
		println(x)
	case <-ch:
		println(0)
	case ch <- 1:
		println(1)
	default:
		println("default")
	}
}

// NB, potentially fragile reliance on register number.
func NonblockingSelectDefaultCasePanics(ch chan int) {
	// switch t1 {
	// case 0:int: extract t0 #2
	// case 1:int: println(0:int)
	// case 2:int: println(1:int)
	// default: make interface{} <- string ("blocking select m...":string)
	// }
	select {
	case x := <-ch:
		println(x)
	case <-ch:
		println(0)
	case ch <- 1:
		println(1)
	}
}

// -------- Type switches --------

// NB, reliance on fragile register numbering.
func SimpleTypeSwitch(x interface{}) {
	// switch x.(type) {
	// case t3 int: println(x)
	// case t7 bool: println(x)
	// case t10 string: println(t10)
	// default: println(x)
	// }
	switch y := x.(type) {
	case nil:
		println(y)
	case int, bool:
		println(y)
	case string:
		println(y)
	default:
		println(y)
	}
}

// NB, potentially fragile reliance on register number.
func DuplicateTypesAreNotEliminated(x interface{}) {
	// switch x.(type) {
	// case t1 string: println(1:int)
	// case t5 interface{}: println(t5)
	// case t9 int: println(3:int)
	// default: return
	// }
	switch y := x.(type) {
	case string:
		println(1)
	case interface{}:
		println(y)
	case int:
		println(3) // unreachable!
	}
}

// NB, potentially fragile reliance on register number.
func AdHocTypeSwitch(x interface{}) {
	// switch x.(type) {
	// case t1 int: println(t1)
	// case t5 string: println(t5)
	// default: print("default":string)
	// }
	if i, ok := x.(int); ok {
		println(i)
	} else if s, ok := x.(string); ok {
		println(s)
	} else {
		print("default")
	}
}
