summaryrefslogtreecommitdiff
path: root/src/pkg/template/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/template/exec.go')
-rw-r--r--src/pkg/template/exec.go63
1 files changed, 41 insertions, 22 deletions
diff --git a/src/pkg/template/exec.go b/src/pkg/template/exec.go
index eaa57ae81..e7fad72fe 100644
--- a/src/pkg/template/exec.go
+++ b/src/pkg/template/exec.go
@@ -196,23 +196,25 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
// mark top of stack before any variables in the body are pushed.
mark := s.mark()
+ oneIteration := func(index, elem reflect.Value) {
+ // Set top var (lexically the second if there are two) to the element.
+ if len(r.Pipe.Decl) > 0 {
+ s.setVar(1, elem)
+ }
+ // Set next var (lexically the first if there are two) to the index.
+ if len(r.Pipe.Decl) > 1 {
+ s.setVar(2, index)
+ }
+ s.walk(elem, r.List)
+ s.pop(mark)
+ }
switch val.Kind() {
case reflect.Array, reflect.Slice:
if val.Len() == 0 {
break
}
for i := 0; i < val.Len(); i++ {
- elem := val.Index(i)
- // Set top var (lexically the second if there are two) to the element.
- if len(r.Pipe.Decl) > 0 {
- s.setVar(1, elem)
- }
- // Set next var (lexically the first if there are two) to the index.
- if len(r.Pipe.Decl) > 1 {
- s.setVar(2, reflect.ValueOf(i))
- }
- s.walk(elem, r.List)
- s.pop(mark)
+ oneIteration(reflect.ValueOf(i), val.Index(i))
}
return
case reflect.Map:
@@ -220,17 +222,23 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
break
}
for _, key := range val.MapKeys() {
- elem := val.MapIndex(key)
- // Set top var (lexically the second if there are two) to the element.
- if len(r.Pipe.Decl) > 0 {
- s.setVar(1, elem)
- }
- // Set next var (lexically the first if there are two) to the key.
- if len(r.Pipe.Decl) > 1 {
- s.setVar(2, key)
+ oneIteration(key, val.MapIndex(key))
+ }
+ return
+ case reflect.Chan:
+ if val.IsNil() {
+ break
+ }
+ i := 0
+ for ; ; i++ {
+ elem, ok := val.Recv()
+ if !ok {
+ break
}
- s.walk(elem, r.List)
- s.pop(mark)
+ oneIteration(reflect.ValueOf(i), elem)
+ }
+ if i == 0 {
+ break
}
return
case reflect.Invalid:
@@ -498,7 +506,18 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
s.errorf("invalid value; expected %s", typ)
}
if !value.Type().AssignableTo(typ) {
- s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
+ // Does one dereference or indirection work? We could do more, as we
+ // do with method receivers, but that gets messy and method receivers
+ // are much more constrained, so it makes more sense there than here.
+ // Besides, one is almost always all you need.
+ switch {
+ case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
+ value = value.Elem()
+ case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
+ value = value.Addr()
+ default:
+ s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
+ }
}
return value
}