// $G $D/$F.go && $L $F.$A && ./$A.out >tmp.go && // $G tmp.go && $L tmp.$A && ./$A.out || echo BUG: select5 // rm -f tmp.go // Copyright 2011 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. // Generate test of channel operations and simple selects. // Only doing one real send or receive at a time, but phrased // in various ways that the compiler may or may not rewrite // into simpler expressions. package main import ( "bufio" "fmt" "io" "os" "template" ) func main() { out := bufio.NewWriter(os.Stdout) fmt.Fprintln(out, header) a := new(arg) // Generate each kind of test as a separate function to avoid // hitting the 6g optimizer with one enormous function. // If we name all the functions init we don't have to // maintain a list of which ones to run. do := func(t *template.Template) { fmt.Fprintln(out, `func init() {`) for ; next(); a.reset() { run(t, a, out) } fmt.Fprintln(out, `}`) } do(recv) do(send) do(recvOrder) do(sendOrder) do(nonblock) fmt.Fprintln(out, "//", a.nreset, "cases") out.Flush() } func run(t *template.Template, a interface{}, out io.Writer) { if err := t.Execute(a, out); err != nil { panic(err) } } type arg struct{ def bool nreset int } func (a *arg) Maybe() bool { return maybe() } func (a *arg) MaybeDefault() bool { if a.def { return false } a.def = maybe() return a.def } func (a *arg) MustDefault() bool { return !a.def } func (a *arg) reset() { a.def = false a.nreset++ } const header = `// GENERATED BY select5.go; DO NOT EDIT package main // channel is buffered so test is single-goroutine. // we are not interested in the concurrency aspects // of select, just testing that the right calls happen. var c = make(chan int, 1) var nilch chan int var n = 1 var x int var i interface{} var dummy = make(chan int) var m = make(map[int]int) var order = 0 func f(p *int) *int { return p } // check order of operations by ensuring that // successive calls to checkorder have increasing o values. func checkorder(o int) { if o <= order { println("invalid order", o, "after", order) panic("order") } order = o } func fc(c chan int, o int) chan int { checkorder(o) return c } func fp(p *int, o int) *int { checkorder(o) return p } func fn(n, o int) int { checkorder(o) return n } func die(x int) { println("have", x, "want", n) panic("chan") } func main() { // everything happens in init funcs } ` func parse(s string) *template.Template { t := template.New(nil) t.SetDelims("〈", "〉") if err := t.Parse(s); err != nil { panic(s) } return t } var recv = parse(` 〈# Send n, receive it one way or another into x, check that they match.〉 c <- n 〈.section Maybe〉 x = <-c 〈.or〉 select { 〈# Blocking or non-blocking, before the receive.〉 〈# The compiler implements two-case select where one is default with custom code,〉 〈# so test the default branch both before and after the send.〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉 〈.section Maybe〉 case x = <-c: 〈.or〉〈.section Maybe〉 case *f(&x) = <-c: 〈.or〉〈.section Maybe〉 case y := <-c: x = y 〈.or〉〈.section Maybe〉 case i = <-c: x = i.(int) 〈.or〉 case m[13] = <-c: x = m[13] 〈.end〉〈.end〉〈.end〉〈.end〉 〈# Blocking or non-blocking again, after the receive.〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Dummy send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case dummy <- 1: panic("dummy send") 〈.end〉 〈.section Maybe〉 case <-dummy: panic("dummy receive") 〈.end〉 〈# Nil channel send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case nilch <- 1: panic("nilch send") 〈.end〉 〈.section Maybe〉 case <-nilch: panic("nilch recv") 〈.end〉 } 〈.end〉 if x != n { die(x) } n++ `) var recvOrder = parse(` 〈# Send n, receive it one way or another into x, check that they match.〉 〈# Check order of operations along the way by calling functions that check〉 〈# that the argument sequence is strictly increasing.〉 order = 0 c <- n 〈.section Maybe〉 〈# Outside of select, left-to-right rule applies.〉 〈# (Inside select, assignment waits until case is chosen,〉 〈# so right hand side happens before anything on left hand side.〉 *fp(&x, 1) = <-fc(c, 2) 〈.or〉〈.section Maybe〉 m[fn(13, 1)] = <-fc(c, 2) x = m[13] 〈.or〉 select { 〈# Blocking or non-blocking, before the receive.〉 〈# The compiler implements two-case select where one is default with custom code,〉 〈# so test the default branch both before and after the send.〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉 〈.section Maybe〉 case *fp(&x, 100) = <-fc(c, 1): 〈.or〉〈.section Maybe〉 case y := <-fc(c, 1): x = y 〈.or〉〈.section Maybe〉 case i = <-fc(c, 1): x = i.(int) 〈.or〉 case m[fn(13, 100)] = <-fc(c, 1): x = m[13] 〈.end〉〈.end〉〈.end〉 〈# Blocking or non-blocking again, after the receive.〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Dummy send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case fc(dummy, 2) <- fn(1, 3): panic("dummy send") 〈.end〉 〈.section Maybe〉 case <-fc(dummy, 4): panic("dummy receive") 〈.end〉 〈# Nil channel send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case fc(nilch, 5) <- fn(1, 6): panic("nilch send") 〈.end〉 〈.section Maybe〉 case <-fc(nilch, 7): panic("nilch recv") 〈.end〉 } 〈.end〉〈.end〉 if x != n { die(x) } n++ `) var send = parse(` 〈# Send n one way or another, receive it into x, check that they match.〉 〈.section Maybe〉 c <- n 〈.or〉 select { 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Send c <- n. No real special cases here, because no values come back〉 〈# from the send operation.〉 case c <- n: 〈# Blocking or non-blocking.〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Dummy send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case dummy <- 1: panic("dummy send") 〈.end〉 〈.section Maybe〉 case <-dummy: panic("dummy receive") 〈.end〉 〈# Nil channel send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case nilch <- 1: panic("nilch send") 〈.end〉 〈.section Maybe〉 case <-nilch: panic("nilch recv") 〈.end〉 } 〈.end〉 x = <-c if x != n { die(x) } n++ `) var sendOrder = parse(` 〈# Send n one way or another, receive it into x, check that they match.〉 〈# Check order of operations along the way by calling functions that check〉 〈# that the argument sequence is strictly increasing.〉 order = 0 〈.section Maybe〉 fc(c, 1) <- fn(n, 2) 〈.or〉 select { 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Send c <- n. No real special cases here, because no values come back〉 〈# from the send operation.〉 case fc(c, 1) <- fn(n, 2): 〈# Blocking or non-blocking.〉 〈.section MaybeDefault〉 default: panic("nonblock") 〈.end〉 〈# Dummy send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case fc(dummy, 3) <- fn(1, 4): panic("dummy send") 〈.end〉 〈.section Maybe〉 case <-fc(dummy, 5): panic("dummy receive") 〈.end〉 〈# Nil channel send, receive to keep compiler from optimizing select.〉 〈.section Maybe〉 case fc(nilch, 6) <- fn(1, 7): panic("nilch send") 〈.end〉 〈.section Maybe〉 case <-fc(nilch, 8): panic("nilch recv") 〈.end〉 } 〈.end〉 x = <-c if x != n { die(x) } n++ `) var nonblock = parse(` x = n 〈# Test various combinations of non-blocking operations.〉 〈# Receive assignments must not edit or even attempt to compute the address of the lhs.〉 select { 〈.section MaybeDefault〉 default: 〈.end〉 〈.section Maybe〉 case dummy <- 1: panic("dummy <- 1") 〈.end〉 〈.section Maybe〉 case nilch <- 1: panic("nilch <- 1") 〈.end〉 〈.section Maybe〉 case <-dummy: panic("<-dummy") 〈.end〉 〈.section Maybe〉 case x = <-dummy: panic("<-dummy x") 〈.end〉 〈.section Maybe〉 case **(**int)(nil) = <-dummy: panic("<-dummy (and didn't crash saving result!)") 〈.end〉 〈.section Maybe〉 case <-nilch: panic("<-nilch") 〈.end〉 〈.section Maybe〉 case x = <-nilch: panic("<-nilch x") 〈.end〉 〈.section Maybe〉 case **(**int)(nil) = <-nilch: panic("<-nilch (and didn't crash saving result!)") 〈.end〉 〈.section MustDefault〉 default: 〈.end〉 } if x != n { die(x) } n++ `) // Code for enumerating all possible paths through // some logic. The logic should call choose(n) when // it wants to choose between n possibilities. // On successive runs through the logic, choose(n) // will return 0, 1, ..., n-1. The helper maybe() is // similar but returns true and then false. // // Given a function gen that generates an output // using choose and maybe, code can generate all // possible outputs using // // for next() { // gen() // } type choice struct { i, n int } var choices []choice var cp int = -1 func maybe() bool { return choose(2) == 0 } func choose(n int) int { if cp >= len(choices) { // never asked this before: start with 0. choices = append(choices, choice{0, n}) cp = len(choices) return 0 } // otherwise give recorded answer if n != choices[cp].n { panic("inconsistent choices") } i := choices[cp].i cp++ return i } func next() bool { if cp < 0 { // start a new round cp = 0 return true } // increment last choice sequence cp = len(choices)-1 for cp >= 0 && choices[cp].i == choices[cp].n-1 { cp-- } if cp < 0 { choices = choices[:0] return false } choices[cp].i++ choices = choices[:cp+1] cp = 0 return true }