diff --git a/test/chan/select5.go b/test/chan/select5.go new file mode 100644 index 00000000000..0678b8dab62 --- /dev/null +++ b/test/chan/select5.go @@ -0,0 +1,482 @@ +// $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 +} +