fmt: cleanup reflect value handling

Merge printReflectValue into printValue. Determine if handleMethods
was already called in printArg by checking if depth is 0. Do not
call handleMethods on depth 0 again in printValue to not introduce
a performance regression. handleMethods is called already in printArg
to not introduce a performance penalty for top-level Stringer,
GoStringer, Errors and Formatters by using reflect.ValueOf on them
just to retrieve them again as interface{} values in printValue.

Clear p.arg in printValue after handleMethods to print the type
of the value inside the reflect.Value when a bad verb is encountered
on the top level instead of printing "reflect.Value=" as the type of
the argument. This also fixes a bug that incorrectly prints the
whole map instead of just the value for a key if the returned value
by the map for the key is an invalid reflect value.

name                     old time/op  new time/op  delta
SprintfPadding-2          229ns ± 2%   227ns ± 1%  -0.50%  (p=0.013 n=20+20)
SprintfEmpty-2           36.4ns ± 6%  37.2ns ±14%    ~     (p=0.091 n=18+20)
SprintfString-2           102ns ± 1%   102ns ± 0%    ~     (p=0.751 n=20+20)
SprintfTruncateString-2   142ns ± 0%   141ns ± 1%  -0.95%  (p=0.000 n=16+20)
SprintfQuoteString-2      389ns ± 0%   388ns ± 0%  -0.12%  (p=0.019 n=20+20)
SprintfInt-2              100ns ± 2%   100ns ± 1%    ~     (p=0.188 n=20+15)
SprintfIntInt-2           155ns ± 3%   154ns ± 2%    ~     (p=0.092 n=20+20)
SprintfPrefixedInt-2      250ns ± 2%   251ns ± 3%    ~     (p=0.559 n=20+20)
SprintfFloat-2            177ns ± 2%   175ns ± 1%  -1.30%  (p=0.000 n=20+20)
SprintfComplex-2          516ns ± 1%   510ns ± 1%  -1.13%  (p=0.000 n=19+16)
SprintfBoolean-2         90.9ns ± 3%  90.6ns ± 1%    ~     (p=0.193 n=19+19)
SprintfHexString-2        171ns ± 1%   169ns ± 1%  -1.44%  (p=0.000 n=19+20)
SprintfHexBytes-2         180ns ± 1%   180ns ± 1%    ~     (p=0.060 n=19+18)
SprintfBytes-2            330ns ± 1%   329ns ± 1%  -0.42%  (p=0.003 n=20+20)
SprintfStringer-2         354ns ± 3%   352ns ± 3%    ~     (p=0.525 n=20+19)
SprintfStructure-2        804ns ± 3%   776ns ± 2%  -3.56%  (p=0.000 n=20+20)
FprintInt-2               155ns ± 0%   151ns ± 1%  -2.35%  (p=0.000 n=19+20)
FprintfBytes-2            169ns ± 0%   170ns ± 1%  +0.81%  (p=0.000 n=18+19)
FprintIntNoAlloc-2        112ns ± 0%   109ns ± 1%  -2.28%  (p=0.000 n=20+20)

Change-Id: Ib9a39082ed1be0f1f7499ee6fb6c9530f043e43a
Reviewed-on: https://go-review.googlesource.com/20923
Run-TryBot: Rob Pike <r@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index be7299c..ffa2499 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -977,6 +977,8 @@
 
 	// invalid reflect.Value doesn't crash.
 	{"%v", reflect.Value{}, "<invalid reflect.Value>"},
+	{"%v", &reflect.Value{}, "<invalid Value>"},
+	{"%v", SI{reflect.Value{}}, "{<invalid Value>}"},
 
 	// Tests to check that not supported verbs generate an error string.
 	{"%☠", nil, "%!☠(<nil>)"},
@@ -995,6 +997,12 @@
 	{"%☠", &intVar, "%!☠(*int=0xPTR)"},
 	{"%☠", make(chan int), "%!☠(chan int=0xPTR)"},
 	{"%☠", func() {}, "%!☠(func()=0xPTR)"},
+	{"%☠", reflect.ValueOf(renamedInt(0)), "%!☠(fmt_test.renamedInt=0)"},
+	{"%☠", SI{renamedInt(0)}, "{%!☠(fmt_test.renamedInt=0)}"},
+	{"%☠", &[]interface{}{I(1), G(2)}, "&[%!☠(fmt_test.I=1) %!☠(fmt_test.G=2)]"},
+	{"%☠", SI{&[]interface{}{I(1), G(2)}}, "{%!☠(*[]interface {}=&[1 2])}"},
+	{"%☠", reflect.Value{}, "<invalid reflect.Value>"},
+	{"%☠", map[float64]int{NaN: 1}, "map[%!☠(float64=NaN):%!☠(<nil>)]"},
 }
 
 // zeroFill generates zero-filled strings of the specified width. The length
@@ -1262,6 +1270,24 @@
 	})
 }
 
+func BenchmarkSprintfStringer(b *testing.B) {
+	stringer := I(12345)
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			Sprintf("%v", stringer)
+		}
+	})
+}
+
+func BenchmarkSprintfStructure(b *testing.B) {
+	s := &[]interface{}{SI{12345}, map[int]string{0: "hello"}}
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			Sprintf("%#v", s)
+		}
+	})
+}
+
 func BenchmarkManyArgs(b *testing.B) {
 	b.RunParallel(func(pb *testing.PB) {
 		var buf bytes.Buffer
diff --git a/src/fmt/print.go b/src/fmt/print.go
index c802232..6c64773 100644
--- a/src/fmt/print.go
+++ b/src/fmt/print.go
@@ -657,57 +657,44 @@
 	case []byte:
 		p.fmtBytes(f, verb, bytesString)
 	case reflect.Value:
-		p.printReflectValue(f, verb, 0)
-		return
+		p.printValue(f, verb, 0)
 	default:
 		// If the type is not simple, it might have methods.
-		if p.handleMethods(verb) {
-			return
+		if !p.handleMethods(verb) {
+			// Need to use reflection, since the type had no
+			// interface methods that could be used for formatting.
+			p.printValue(reflect.ValueOf(f), verb, 0)
 		}
-		// Need to use reflection
-		p.printReflectValue(reflect.ValueOf(arg), verb, 0)
-		return
 	}
-	p.arg = nil
-}
-
-// printValue is similar to printArg but starts with a reflect value, not an interface{} value.
-// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg.
-func (p *pp) printValue(value reflect.Value, verb rune, depth int) {
-	if !value.IsValid() {
-		switch verb {
-		case 'v':
-			p.buf.WriteString(nilAngleString)
-		default:
-			p.badVerb(verb)
-		}
-		return
-	}
-
-	// Handle values with special methods.
-	// Call always, even when arg == nil, because handleMethods clears p.fmt.plus for us.
-	p.arg = nil // Make sure it's cleared, for safety.
-	if value.CanInterface() {
-		p.arg = value.Interface()
-	}
-	if p.handleMethods(verb) {
-		return
-	}
-
-	p.printReflectValue(value, verb, depth)
 }
 
 var byteType = reflect.TypeOf(byte(0))
 
-// printReflectValue is the fallback for both printArg and printValue.
-// It uses reflect to print the value.
-func (p *pp) printReflectValue(value reflect.Value, verb rune, depth int) {
-	oldValue := p.value
+// printValue is similar to printArg but starts with a reflect value, not an interface{} value.
+// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg.
+func (p *pp) printValue(value reflect.Value, verb rune, depth int) {
+	// Handle values with special methods if not already handled by printArg (depth == 0).
+	if depth > 0 && value.IsValid() && value.CanInterface() {
+		p.arg = value.Interface()
+		if p.handleMethods(verb) {
+			return
+		}
+	}
+	p.arg = nil
 	p.value = value
-BigSwitch:
-	switch f := value; f.Kind() {
+
+	switch f := value; value.Kind() {
 	case reflect.Invalid:
-		p.buf.WriteString(invReflectString)
+		if depth == 0 {
+			p.buf.WriteString(invReflectString)
+		} else {
+			switch verb {
+			case 'v':
+				p.buf.WriteString(nilAngleString)
+			default:
+				p.badVerb(verb)
+			}
+		}
 	case reflect.Bool:
 		p.fmtBool(f.Bool(), verb)
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -729,7 +716,7 @@
 			p.buf.WriteString(f.Type().String())
 			if f.IsNil() {
 				p.buf.WriteString(nilParenString)
-				break
+				return
 			}
 			p.buf.WriteByte('{')
 		} else {
@@ -755,12 +742,10 @@
 		}
 	case reflect.Struct:
 		if p.fmt.sharpV {
-			p.buf.WriteString(value.Type().String())
+			p.buf.WriteString(f.Type().String())
 		}
 		p.buf.WriteByte('{')
-		v := f
-		t := v.Type()
-		for i := 0; i < v.NumField(); i++ {
+		for i := 0; i < f.NumField(); i++ {
 			if i > 0 {
 				if p.fmt.sharpV {
 					p.buf.WriteString(commaSpaceString)
@@ -769,12 +754,12 @@
 				}
 			}
 			if p.fmt.plusV || p.fmt.sharpV {
-				if f := t.Field(i); f.Name != "" {
-					p.buf.WriteString(f.Name)
+				if name := f.Type().Field(i).Name; name != "" {
+					p.buf.WriteString(name)
 					p.buf.WriteByte(':')
 				}
 			}
-			p.printValue(getField(v, i), verb, depth+1)
+			p.printValue(getField(f, i), verb, depth+1)
 		}
 		p.buf.WriteByte('}')
 	case reflect.Interface:
@@ -813,13 +798,13 @@
 				}
 			}
 			p.fmtBytes(bytes, verb, typ.String())
-			break
+			return
 		}
 		if p.fmt.sharpV {
 			p.buf.WriteString(typ.String())
 			if f.Kind() == reflect.Slice && f.IsNil() {
 				p.buf.WriteString(nilParenString)
-				break
+				return
 			}
 			p.buf.WriteByte('{')
 		} else {
@@ -841,32 +826,22 @@
 			p.buf.WriteByte(']')
 		}
 	case reflect.Ptr:
-		v := f.Pointer()
 		// pointer to array or slice or struct?  ok at top level
 		// but not embedded (avoid loops)
-		if v != 0 && depth == 0 {
+		if depth == 0 && f.Pointer() != 0 {
 			switch a := f.Elem(); a.Kind() {
-			case reflect.Array, reflect.Slice:
+			case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
 				p.buf.WriteByte('&')
 				p.printValue(a, verb, depth+1)
-				break BigSwitch
-			case reflect.Struct:
-				p.buf.WriteByte('&')
-				p.printValue(a, verb, depth+1)
-				break BigSwitch
-			case reflect.Map:
-				p.buf.WriteByte('&')
-				p.printValue(a, verb, depth+1)
-				break BigSwitch
+				return
 			}
 		}
 		fallthrough
 	case reflect.Chan, reflect.Func, reflect.UnsafePointer:
-		p.fmtPointer(value, verb)
+		p.fmtPointer(f, verb)
 	default:
 		p.unknownType(f)
 	}
-	p.value = oldValue
 }
 
 // intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type.