reflect: add Select

R=r, iant, rogpeppe, bradfitz
CC=golang-dev
https://golang.org/cl/6498078
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index c44d408..9b01646 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -1618,6 +1618,140 @@
 	return n
 }
 
+// A runtimeSelect is a single case passed to rselect.
+// This must match ../runtime/chan.c:/runtimeSelect
+type runtimeSelect struct {
+	dir uintptr      // 0, SendDir, or RecvDir
+	typ *runtimeType // channel type
+	ch  iword        // interface word for channel
+	val iword        // interface word for value (for SendDir)
+}
+
+// rselect runs a select. It returns the index of the chosen case,
+// and if the case was a receive, the interface word of the received
+// value and the conventional OK bool to indicate whether the receive
+// corresponds to a sent value.
+func rselect([]runtimeSelect) (chosen int, recv iword, recvOK bool)
+
+// A SelectDir describes the communication direction of a select case.
+type SelectDir int
+
+// NOTE: These values must match ../runtime/chan.c:/SelectDir.
+
+const (
+	_             SelectDir = iota
+	SelectSend              // case Chan <- Send
+	SelectRecv              // case <-Chan:
+	SelectDefault           // default
+)
+
+// A SelectCase describes a single case in a select operation.
+// The kind of case depends on Dir, the communication direction.
+//
+// If Dir is SelectDefault, the case represents a default case.
+// Chan and Send must be zero Values.
+//
+// If Dir is SelectSend, the case represents a send operation.
+// Normally Chan's underlying value must be a channel, and Send's underlying value must be
+// assignable to the channel's element type. As a special case, if Chan is a zero Value,
+// then the case is ignored, and the field Send will also be ignored and may be either zero
+// or non-zero.
+//
+// If Dir is SelectRecv, the case represents a receive operation.
+// Normally Chan's underlying value must be a channel and Send must be a zero Value.
+// If Chan is a zero Value, then the case is ignored, but Send must still be a zero Value.
+// When a receive operation is selected, the received Value is returned by Select.
+//
+type SelectCase struct {
+	Dir  SelectDir // direction of case
+	Chan Value     // channel to use (for send or receive)
+	Send Value     // value to send (for send)
+}
+
+// Select executes a select operation described by the list of cases.
+// Like the Go select statement, it blocks until one of the cases can
+// proceed and then executes that case. It returns the index of the chosen case
+// and, if that case was a receive operation, the value received and a
+// boolean indicating whether the value corresponds to a send on the channel
+// (as opposed to a zero value received because the channel is closed).
+func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
+	// NOTE: Do not trust that caller is not modifying cases data underfoot.
+	// The range is safe because the caller cannot modify our copy of the len
+	// and each iteration makes its own copy of the value c.
+	runcases := make([]runtimeSelect, len(cases))
+	haveDefault := false
+	for i, c := range cases {
+		rc := &runcases[i]
+		rc.dir = uintptr(c.Dir)
+		switch c.Dir {
+		default:
+			panic("reflect.Select: invalid Dir")
+
+		case SelectDefault: // default	
+			if haveDefault {
+				panic("reflect.Select: multiple default cases")
+			}
+			haveDefault = true
+			if c.Chan.IsValid() {
+				panic("reflect.Select: default case has Chan value")
+			}
+			if c.Send.IsValid() {
+				panic("reflect.Select: default case has Send value")
+			}
+
+		case SelectSend:
+			ch := c.Chan
+			if !ch.IsValid() {
+				break
+			}
+			ch.mustBe(Chan)
+			ch.mustBeExported()
+			tt := (*chanType)(unsafe.Pointer(ch.typ))
+			if ChanDir(tt.dir)&SendDir == 0 {
+				panic("reflect.Select: SendDir case using recv-only channel")
+			}
+			rc.ch = ch.iword()
+			rc.typ = tt.runtimeType()
+			v := c.Send
+			if !v.IsValid() {
+				panic("reflect.Select: SendDir case missing Send value")
+			}
+			v.mustBeExported()
+			v = v.assignTo("reflect.Select", toCommonType(tt.elem), nil)
+			rc.val = v.iword()
+
+		case SelectRecv:
+			if c.Send.IsValid() {
+				panic("reflect.Select: RecvDir case has Send value")
+			}
+			ch := c.Chan
+			if !ch.IsValid() {
+				break
+			}
+			ch.mustBe(Chan)
+			ch.mustBeExported()
+			tt := (*chanType)(unsafe.Pointer(ch.typ))
+			rc.typ = tt.runtimeType()
+			if ChanDir(tt.dir)&RecvDir == 0 {
+				panic("reflect.Select: RecvDir case using send-only channel")
+			}
+			rc.ch = ch.iword()
+		}
+	}
+
+	chosen, word, recvOK := rselect(runcases)
+	if runcases[chosen].dir == uintptr(SelectRecv) {
+		tt := (*chanType)(unsafe.Pointer(toCommonType(runcases[chosen].typ)))
+		typ := toCommonType(tt.elem)
+		fl := flag(typ.Kind()) << flagKindShift
+		if typ.size > ptrSize {
+			fl |= flagIndir
+		}
+		recv = Value{typ, unsafe.Pointer(word), fl}
+	}
+	return chosen, recv, recvOK
+}
+
 /*
  * constructors
  */