reflect/protoreflect: add non-allocating Value constructors

Passing a non-pointer type to protoreflect.NewValue causes an
unnecessary allocation in order to store the value in an interface{}.
While this allocation could be avoided by a smarter compiler, no such
compiler exists today.

Add functions for creating new values of a specific type, avoiding the
allocation. (And also adding a small amount of type safety, although
this is unlikely to be important.)

Update the proto and internal/impl packages to use these functions.

Change-Id: Ic733de22ddf19c530189166c853348e1b54b7391
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/191457
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/cmd/generate-types/proto.go b/internal/cmd/generate-types/proto.go
index 5cbf4ce..1cd0ed7 100644
--- a/internal/cmd/generate-types/proto.go
+++ b/internal/cmd/generate-types/proto.go
@@ -90,6 +90,7 @@
 	ToGoTypeNoZero Expr
 	FromGoType     Expr
 	NoPointer      bool
+	NoValueCodec   bool
 }
 
 func (k ProtoKind) Expr() Expr {
@@ -100,7 +101,7 @@
 	{
 		Name:       "Bool",
 		WireType:   WireVarint,
-		ToValue:    "wire.DecodeBool(v)",
+		ToValue:    "protoreflect.ValueOfBool(wire.DecodeBool(v))",
 		FromValue:  "wire.EncodeBool(v.Bool())",
 		GoType:     GoBool,
 		ToGoType:   "wire.DecodeBool(v)",
@@ -109,13 +110,13 @@
 	{
 		Name:      "Enum",
 		WireType:  WireVarint,
-		ToValue:   "protoreflect.EnumNumber(v)",
+		ToValue:   "protoreflect.ValueOfEnum(protoreflect.EnumNumber(v))",
 		FromValue: "uint64(v.Enum())",
 	},
 	{
 		Name:       "Int32",
 		WireType:   WireVarint,
-		ToValue:    "int32(v)",
+		ToValue:    "protoreflect.ValueOfInt32(int32(v))",
 		FromValue:  "uint64(int32(v.Int()))",
 		GoType:     GoInt32,
 		ToGoType:   "int32(v)",
@@ -124,7 +125,7 @@
 	{
 		Name:       "Sint32",
 		WireType:   WireVarint,
-		ToValue:    "int32(wire.DecodeZigZag(v & math.MaxUint32))",
+		ToValue:    "protoreflect.ValueOfInt32(int32(wire.DecodeZigZag(v & math.MaxUint32)))",
 		FromValue:  "wire.EncodeZigZag(int64(int32(v.Int())))",
 		GoType:     GoInt32,
 		ToGoType:   "int32(wire.DecodeZigZag(v & math.MaxUint32))",
@@ -133,7 +134,7 @@
 	{
 		Name:       "Uint32",
 		WireType:   WireVarint,
-		ToValue:    "uint32(v)",
+		ToValue:    "protoreflect.ValueOfUint32(uint32(v))",
 		FromValue:  "uint64(uint32(v.Uint()))",
 		GoType:     GoUint32,
 		ToGoType:   "uint32(v)",
@@ -142,7 +143,7 @@
 	{
 		Name:       "Int64",
 		WireType:   WireVarint,
-		ToValue:    "int64(v)",
+		ToValue:    "protoreflect.ValueOfInt64(int64(v))",
 		FromValue:  "uint64(v.Int())",
 		GoType:     GoInt64,
 		ToGoType:   "int64(v)",
@@ -151,7 +152,7 @@
 	{
 		Name:       "Sint64",
 		WireType:   WireVarint,
-		ToValue:    "wire.DecodeZigZag(v)",
+		ToValue:    "protoreflect.ValueOfInt64(wire.DecodeZigZag(v))",
 		FromValue:  "wire.EncodeZigZag(v.Int())",
 		GoType:     GoInt64,
 		ToGoType:   "wire.DecodeZigZag(v)",
@@ -160,7 +161,7 @@
 	{
 		Name:       "Uint64",
 		WireType:   WireVarint,
-		ToValue:    "v",
+		ToValue:    "protoreflect.ValueOfUint64(v)",
 		FromValue:  "v.Uint()",
 		GoType:     GoUint64,
 		ToGoType:   "v",
@@ -169,7 +170,7 @@
 	{
 		Name:       "Sfixed32",
 		WireType:   WireFixed32,
-		ToValue:    "int32(v)",
+		ToValue:    "protoreflect.ValueOfInt32(int32(v))",
 		FromValue:  "uint32(v.Int())",
 		GoType:     GoInt32,
 		ToGoType:   "int32(v)",
@@ -178,7 +179,7 @@
 	{
 		Name:       "Fixed32",
 		WireType:   WireFixed32,
-		ToValue:    "uint32(v)",
+		ToValue:    "protoreflect.ValueOfUint32(uint32(v))",
 		FromValue:  "uint32(v.Uint())",
 		GoType:     GoUint32,
 		ToGoType:   "v",
@@ -187,7 +188,7 @@
 	{
 		Name:       "Float",
 		WireType:   WireFixed32,
-		ToValue:    "math.Float32frombits(uint32(v))",
+		ToValue:    "protoreflect.ValueOfFloat32(math.Float32frombits(uint32(v)))",
 		FromValue:  "math.Float32bits(float32(v.Float()))",
 		GoType:     GoFloat32,
 		ToGoType:   "math.Float32frombits(v)",
@@ -196,7 +197,7 @@
 	{
 		Name:       "Sfixed64",
 		WireType:   WireFixed64,
-		ToValue:    "int64(v)",
+		ToValue:    "protoreflect.ValueOfInt64(int64(v))",
 		FromValue:  "uint64(v.Int())",
 		GoType:     GoInt64,
 		ToGoType:   "int64(v)",
@@ -205,7 +206,7 @@
 	{
 		Name:       "Fixed64",
 		WireType:   WireFixed64,
-		ToValue:    "v",
+		ToValue:    "protoreflect.ValueOfUint64(v)",
 		FromValue:  "v.Uint()",
 		GoType:     GoUint64,
 		ToGoType:   "v",
@@ -214,7 +215,7 @@
 	{
 		Name:       "Double",
 		WireType:   WireFixed64,
-		ToValue:    "math.Float64frombits(v)",
+		ToValue:    "protoreflect.ValueOfFloat64(math.Float64frombits(v))",
 		FromValue:  "math.Float64bits(v.Float())",
 		GoType:     GoFloat64,
 		ToGoType:   "math.Float64frombits(v)",
@@ -223,7 +224,7 @@
 	{
 		Name:       "String",
 		WireType:   WireBytes,
-		ToValue:    "string(v)",
+		ToValue:    "protoreflect.ValueOfString(string(v))",
 		FromValue:  "v.String()",
 		GoType:     GoString,
 		ToGoType:   "v",
@@ -232,7 +233,7 @@
 	{
 		Name:           "Bytes",
 		WireType:       WireBytes,
-		ToValue:        "append(([]byte)(nil), v...)",
+		ToValue:        "protoreflect.ValueOfBytes(append(([]byte)(nil), v...))",
 		FromValue:      "v.Bytes()",
 		GoType:         GoBytes,
 		ToGoType:       "append(emptyBuf[:], v...)",
@@ -241,16 +242,18 @@
 		NoPointer:      true,
 	},
 	{
-		Name:      "Message",
-		WireType:  WireBytes,
-		ToValue:   "v",
-		FromValue: "v",
+		Name:         "Message",
+		WireType:     WireBytes,
+		ToValue:      "protoreflect.ValueOfBytes(v)",
+		FromValue:    "v",
+		NoValueCodec: true,
 	},
 	{
-		Name:      "Group",
-		WireType:  WireGroup,
-		ToValue:   "v",
-		FromValue: "v",
+		Name:         "Group",
+		WireType:     WireGroup,
+		ToValue:      "protoreflect.ValueOfBytes(v)",
+		FromValue:    "v",
+		NoValueCodec: true,
 	},
 }
 
@@ -282,7 +285,7 @@
 			return protoreflect.Value{}, 0, errors.InvalidUTF8(string(fd.FullName()))
 		}
 		{{end -}}
-		return protoreflect.ValueOf({{.ToValue}}), n, nil
+		return {{.ToValue}}, n, nil
 	{{- end}}
 	default:
 		return val, 0, errUnknown
@@ -305,7 +308,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf({{.ToValue}}))
+				list.Append({{.ToValue}})
 			}
 			return n, nil
 		}
@@ -333,7 +336,7 @@
 		}
 		list.Append(protoreflect.ValueOf(m))
 		{{- else -}}
-		list.Append(protoreflect.ValueOf({{.ToValue}}))
+		list.Append({{.ToValue}})
 		{{- end}}
 		return n, nil
 	{{- end}}
diff --git a/internal/impl/convert.go b/internal/impl/convert.go
index 4b280ef..9b74c03 100644
--- a/internal/impl/convert.go
+++ b/internal/impl/convert.go
@@ -81,11 +81,6 @@
 	bytesZero   = pref.ValueOf([]byte(nil))
 )
 
-type scalarConverter struct {
-	goType, pbType reflect.Type
-	def            pref.Value
-}
-
 func newSingularConverter(t reflect.Type, fd pref.FieldDescriptor) Converter {
 	defVal := func(fd pref.FieldDescriptor, zero pref.Value) pref.Value {
 		if fd.Cardinality() == pref.Repeated {
@@ -97,39 +92,39 @@
 	switch fd.Kind() {
 	case pref.BoolKind:
 		if t.Kind() == reflect.Bool {
-			return &scalarConverter{t, boolType, defVal(fd, boolZero)}
+			return &boolConverter{t, defVal(fd, boolZero)}
 		}
 	case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
 		if t.Kind() == reflect.Int32 {
-			return &scalarConverter{t, int32Type, defVal(fd, int32Zero)}
+			return &int32Converter{t, defVal(fd, int32Zero)}
 		}
 	case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
 		if t.Kind() == reflect.Int64 {
-			return &scalarConverter{t, int64Type, defVal(fd, int64Zero)}
+			return &int64Converter{t, defVal(fd, int64Zero)}
 		}
 	case pref.Uint32Kind, pref.Fixed32Kind:
 		if t.Kind() == reflect.Uint32 {
-			return &scalarConverter{t, uint32Type, defVal(fd, uint32Zero)}
+			return &uint32Converter{t, defVal(fd, uint32Zero)}
 		}
 	case pref.Uint64Kind, pref.Fixed64Kind:
 		if t.Kind() == reflect.Uint64 {
-			return &scalarConverter{t, uint64Type, defVal(fd, uint64Zero)}
+			return &uint64Converter{t, defVal(fd, uint64Zero)}
 		}
 	case pref.FloatKind:
 		if t.Kind() == reflect.Float32 {
-			return &scalarConverter{t, float32Type, defVal(fd, float32Zero)}
+			return &float32Converter{t, defVal(fd, float32Zero)}
 		}
 	case pref.DoubleKind:
 		if t.Kind() == reflect.Float64 {
-			return &scalarConverter{t, float64Type, defVal(fd, float64Zero)}
+			return &float64Converter{t, defVal(fd, float64Zero)}
 		}
 	case pref.StringKind:
 		if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
-			return &scalarConverter{t, stringType, defVal(fd, stringZero)}
+			return &stringConverter{t, defVal(fd, stringZero)}
 		}
 	case pref.BytesKind:
 		if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
-			return &scalarConverter{t, bytesType, defVal(fd, bytesZero)}
+			return &bytesConverter{t, defVal(fd, bytesZero)}
 		}
 	case pref.EnumKind:
 		// Handle enums, which must be a named int32 type.
@@ -142,37 +137,167 @@
 	panic(fmt.Sprintf("invalid Go type %v for field %v", t, fd.FullName()))
 }
 
-func (c *scalarConverter) PBValueOf(v reflect.Value) pref.Value {
+type boolConverter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *boolConverter) PBValueOf(v reflect.Value) pref.Value {
 	if v.Type() != c.goType {
 		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
 	}
-	if c.goType.Kind() == reflect.String && c.pbType.Kind() == reflect.Slice && v.Len() == 0 {
-		return pref.ValueOf([]byte(nil)) // ensure empty string is []byte(nil)
-	}
-	return pref.ValueOf(v.Convert(c.pbType).Interface())
+	return pref.ValueOfBool(v.Bool())
+}
+func (c *boolConverter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(v.Bool()).Convert(c.goType)
+}
+func (c *boolConverter) New() pref.Value  { return c.def }
+func (c *boolConverter) Zero() pref.Value { return c.def }
+
+type int32Converter struct {
+	goType reflect.Type
+	def    pref.Value
 }
 
-func (c *scalarConverter) GoValueOf(v pref.Value) reflect.Value {
-	rv := reflect.ValueOf(v.Interface())
-	if rv.Type() != c.pbType {
-		panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), c.pbType))
+func (c *int32Converter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
 	}
-	if c.pbType.Kind() == reflect.String && c.goType.Kind() == reflect.Slice && rv.Len() == 0 {
+	return pref.ValueOfInt32(int32(v.Int()))
+}
+func (c *int32Converter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(int32(v.Int())).Convert(c.goType)
+}
+func (c *int32Converter) New() pref.Value  { return c.def }
+func (c *int32Converter) Zero() pref.Value { return c.def }
+
+type int64Converter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *int64Converter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
+	}
+	return pref.ValueOfInt64(int64(v.Int()))
+}
+func (c *int64Converter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(int64(v.Int())).Convert(c.goType)
+}
+func (c *int64Converter) New() pref.Value  { return c.def }
+func (c *int64Converter) Zero() pref.Value { return c.def }
+
+type uint32Converter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *uint32Converter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
+	}
+	return pref.ValueOfUint32(uint32(v.Uint()))
+}
+func (c *uint32Converter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(uint32(v.Uint())).Convert(c.goType)
+}
+func (c *uint32Converter) New() pref.Value  { return c.def }
+func (c *uint32Converter) Zero() pref.Value { return c.def }
+
+type uint64Converter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *uint64Converter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
+	}
+	return pref.ValueOfUint64(uint64(v.Uint()))
+}
+func (c *uint64Converter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(uint64(v.Uint())).Convert(c.goType)
+}
+func (c *uint64Converter) New() pref.Value  { return c.def }
+func (c *uint64Converter) Zero() pref.Value { return c.def }
+
+type float32Converter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *float32Converter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
+	}
+	return pref.ValueOfFloat32(float32(v.Float()))
+}
+func (c *float32Converter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(float32(v.Float())).Convert(c.goType)
+}
+func (c *float32Converter) New() pref.Value  { return c.def }
+func (c *float32Converter) Zero() pref.Value { return c.def }
+
+type float64Converter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *float64Converter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
+	}
+	return pref.ValueOfFloat64(float64(v.Float()))
+}
+func (c *float64Converter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(float64(v.Float())).Convert(c.goType)
+}
+func (c *float64Converter) New() pref.Value  { return c.def }
+func (c *float64Converter) Zero() pref.Value { return c.def }
+
+type stringConverter struct {
+	goType reflect.Type
+	def    pref.Value
+}
+
+func (c *stringConverter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
+	}
+	return pref.ValueOfString(v.Convert(stringType).String())
+}
+func (c *stringConverter) GoValueOf(v pref.Value) reflect.Value {
+	// pref.Value.String never panics, so we go through an interface
+	// conversion here to check the type.
+	s := v.Interface().(string)
+	if c.goType.Kind() == reflect.Slice && s == "" {
 		return reflect.Zero(c.goType) // ensure empty string is []byte(nil)
 	}
-	return rv.Convert(c.goType)
+	return reflect.ValueOf(s).Convert(c.goType)
+}
+func (c *stringConverter) New() pref.Value  { return c.def }
+func (c *stringConverter) Zero() pref.Value { return c.def }
+
+type bytesConverter struct {
+	goType reflect.Type
+	def    pref.Value
 }
 
-func (c *scalarConverter) New() pref.Value {
-	if c.pbType == bytesType {
-		return pref.ValueOf(append(([]byte)(nil), c.def.Bytes()...))
+func (c *bytesConverter) PBValueOf(v reflect.Value) pref.Value {
+	if v.Type() != c.goType {
+		panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
 	}
-	return c.def
+	if c.goType.Kind() == reflect.String && v.Len() == 0 {
+		return pref.ValueOfBytes(nil) // ensure empty string is []byte(nil)
+	}
+	return pref.ValueOfBytes(v.Convert(bytesType).Bytes())
 }
-
-func (c *scalarConverter) Zero() pref.Value {
-	return c.New()
+func (c *bytesConverter) GoValueOf(v pref.Value) reflect.Value {
+	return reflect.ValueOf(v.Bytes()).Convert(c.goType)
 }
+func (c *bytesConverter) New() pref.Value  { return c.def }
+func (c *bytesConverter) Zero() pref.Value { return c.def }
 
 type enumConverter struct {
 	goType reflect.Type
diff --git a/proto/decode_gen.go b/proto/decode_gen.go
index dbb4c87..ce53dcd 100644
--- a/proto/decode_gen.go
+++ b/proto/decode_gen.go
@@ -29,7 +29,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(wire.DecodeBool(v)), n, nil
+		return protoreflect.ValueOfBool(wire.DecodeBool(v)), n, nil
 	case protoreflect.EnumKind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -38,7 +38,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(protoreflect.EnumNumber(v)), n, nil
+		return protoreflect.ValueOfEnum(protoreflect.EnumNumber(v)), n, nil
 	case protoreflect.Int32Kind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -47,7 +47,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(int32(v)), n, nil
+		return protoreflect.ValueOfInt32(int32(v)), n, nil
 	case protoreflect.Sint32Kind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -56,7 +56,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(int32(wire.DecodeZigZag(v & math.MaxUint32))), n, nil
+		return protoreflect.ValueOfInt32(int32(wire.DecodeZigZag(v & math.MaxUint32))), n, nil
 	case protoreflect.Uint32Kind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -65,7 +65,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(uint32(v)), n, nil
+		return protoreflect.ValueOfUint32(uint32(v)), n, nil
 	case protoreflect.Int64Kind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -74,7 +74,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(int64(v)), n, nil
+		return protoreflect.ValueOfInt64(int64(v)), n, nil
 	case protoreflect.Sint64Kind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -83,7 +83,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(wire.DecodeZigZag(v)), n, nil
+		return protoreflect.ValueOfInt64(wire.DecodeZigZag(v)), n, nil
 	case protoreflect.Uint64Kind:
 		if wtyp != wire.VarintType {
 			return val, 0, errUnknown
@@ -92,7 +92,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(v), n, nil
+		return protoreflect.ValueOfUint64(v), n, nil
 	case protoreflect.Sfixed32Kind:
 		if wtyp != wire.Fixed32Type {
 			return val, 0, errUnknown
@@ -101,7 +101,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(int32(v)), n, nil
+		return protoreflect.ValueOfInt32(int32(v)), n, nil
 	case protoreflect.Fixed32Kind:
 		if wtyp != wire.Fixed32Type {
 			return val, 0, errUnknown
@@ -110,7 +110,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(uint32(v)), n, nil
+		return protoreflect.ValueOfUint32(uint32(v)), n, nil
 	case protoreflect.FloatKind:
 		if wtyp != wire.Fixed32Type {
 			return val, 0, errUnknown
@@ -119,7 +119,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(math.Float32frombits(uint32(v))), n, nil
+		return protoreflect.ValueOfFloat32(math.Float32frombits(uint32(v))), n, nil
 	case protoreflect.Sfixed64Kind:
 		if wtyp != wire.Fixed64Type {
 			return val, 0, errUnknown
@@ -128,7 +128,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(int64(v)), n, nil
+		return protoreflect.ValueOfInt64(int64(v)), n, nil
 	case protoreflect.Fixed64Kind:
 		if wtyp != wire.Fixed64Type {
 			return val, 0, errUnknown
@@ -137,7 +137,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(v), n, nil
+		return protoreflect.ValueOfUint64(v), n, nil
 	case protoreflect.DoubleKind:
 		if wtyp != wire.Fixed64Type {
 			return val, 0, errUnknown
@@ -146,7 +146,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(math.Float64frombits(v)), n, nil
+		return protoreflect.ValueOfFloat64(math.Float64frombits(v)), n, nil
 	case protoreflect.StringKind:
 		if wtyp != wire.BytesType {
 			return val, 0, errUnknown
@@ -158,7 +158,7 @@
 		if strs.EnforceUTF8(fd) && !utf8.Valid(v) {
 			return protoreflect.Value{}, 0, errors.InvalidUTF8(string(fd.FullName()))
 		}
-		return protoreflect.ValueOf(string(v)), n, nil
+		return protoreflect.ValueOfString(string(v)), n, nil
 	case protoreflect.BytesKind:
 		if wtyp != wire.BytesType {
 			return val, 0, errUnknown
@@ -167,7 +167,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(append(([]byte)(nil), v...)), n, nil
+		return protoreflect.ValueOfBytes(append(([]byte)(nil), v...)), n, nil
 	case protoreflect.MessageKind:
 		if wtyp != wire.BytesType {
 			return val, 0, errUnknown
@@ -176,7 +176,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(v), n, nil
+		return protoreflect.ValueOfBytes(v), n, nil
 	case protoreflect.GroupKind:
 		if wtyp != wire.StartGroupType {
 			return val, 0, errUnknown
@@ -185,7 +185,7 @@
 		if n < 0 {
 			return val, 0, wire.ParseError(n)
 		}
-		return protoreflect.ValueOf(v), n, nil
+		return protoreflect.ValueOfBytes(v), n, nil
 	default:
 		return val, 0, errUnknown
 	}
@@ -205,7 +205,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(wire.DecodeBool(v)))
+				list.Append(protoreflect.ValueOfBool(wire.DecodeBool(v)))
 			}
 			return n, nil
 		}
@@ -216,7 +216,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(wire.DecodeBool(v)))
+		list.Append(protoreflect.ValueOfBool(wire.DecodeBool(v)))
 		return n, nil
 	case protoreflect.EnumKind:
 		if wtyp == wire.BytesType {
@@ -230,7 +230,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(protoreflect.EnumNumber(v)))
+				list.Append(protoreflect.ValueOfEnum(protoreflect.EnumNumber(v)))
 			}
 			return n, nil
 		}
@@ -241,7 +241,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(protoreflect.EnumNumber(v)))
+		list.Append(protoreflect.ValueOfEnum(protoreflect.EnumNumber(v)))
 		return n, nil
 	case protoreflect.Int32Kind:
 		if wtyp == wire.BytesType {
@@ -255,7 +255,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(int32(v)))
+				list.Append(protoreflect.ValueOfInt32(int32(v)))
 			}
 			return n, nil
 		}
@@ -266,7 +266,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(int32(v)))
+		list.Append(protoreflect.ValueOfInt32(int32(v)))
 		return n, nil
 	case protoreflect.Sint32Kind:
 		if wtyp == wire.BytesType {
@@ -280,7 +280,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(int32(wire.DecodeZigZag(v & math.MaxUint32))))
+				list.Append(protoreflect.ValueOfInt32(int32(wire.DecodeZigZag(v & math.MaxUint32))))
 			}
 			return n, nil
 		}
@@ -291,7 +291,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(int32(wire.DecodeZigZag(v & math.MaxUint32))))
+		list.Append(protoreflect.ValueOfInt32(int32(wire.DecodeZigZag(v & math.MaxUint32))))
 		return n, nil
 	case protoreflect.Uint32Kind:
 		if wtyp == wire.BytesType {
@@ -305,7 +305,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(uint32(v)))
+				list.Append(protoreflect.ValueOfUint32(uint32(v)))
 			}
 			return n, nil
 		}
@@ -316,7 +316,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(uint32(v)))
+		list.Append(protoreflect.ValueOfUint32(uint32(v)))
 		return n, nil
 	case protoreflect.Int64Kind:
 		if wtyp == wire.BytesType {
@@ -330,7 +330,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(int64(v)))
+				list.Append(protoreflect.ValueOfInt64(int64(v)))
 			}
 			return n, nil
 		}
@@ -341,7 +341,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(int64(v)))
+		list.Append(protoreflect.ValueOfInt64(int64(v)))
 		return n, nil
 	case protoreflect.Sint64Kind:
 		if wtyp == wire.BytesType {
@@ -355,7 +355,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(wire.DecodeZigZag(v)))
+				list.Append(protoreflect.ValueOfInt64(wire.DecodeZigZag(v)))
 			}
 			return n, nil
 		}
@@ -366,7 +366,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(wire.DecodeZigZag(v)))
+		list.Append(protoreflect.ValueOfInt64(wire.DecodeZigZag(v)))
 		return n, nil
 	case protoreflect.Uint64Kind:
 		if wtyp == wire.BytesType {
@@ -380,7 +380,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(v))
+				list.Append(protoreflect.ValueOfUint64(v))
 			}
 			return n, nil
 		}
@@ -391,7 +391,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(v))
+		list.Append(protoreflect.ValueOfUint64(v))
 		return n, nil
 	case protoreflect.Sfixed32Kind:
 		if wtyp == wire.BytesType {
@@ -405,7 +405,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(int32(v)))
+				list.Append(protoreflect.ValueOfInt32(int32(v)))
 			}
 			return n, nil
 		}
@@ -416,7 +416,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(int32(v)))
+		list.Append(protoreflect.ValueOfInt32(int32(v)))
 		return n, nil
 	case protoreflect.Fixed32Kind:
 		if wtyp == wire.BytesType {
@@ -430,7 +430,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(uint32(v)))
+				list.Append(protoreflect.ValueOfUint32(uint32(v)))
 			}
 			return n, nil
 		}
@@ -441,7 +441,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(uint32(v)))
+		list.Append(protoreflect.ValueOfUint32(uint32(v)))
 		return n, nil
 	case protoreflect.FloatKind:
 		if wtyp == wire.BytesType {
@@ -455,7 +455,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(math.Float32frombits(uint32(v))))
+				list.Append(protoreflect.ValueOfFloat32(math.Float32frombits(uint32(v))))
 			}
 			return n, nil
 		}
@@ -466,7 +466,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(math.Float32frombits(uint32(v))))
+		list.Append(protoreflect.ValueOfFloat32(math.Float32frombits(uint32(v))))
 		return n, nil
 	case protoreflect.Sfixed64Kind:
 		if wtyp == wire.BytesType {
@@ -480,7 +480,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(int64(v)))
+				list.Append(protoreflect.ValueOfInt64(int64(v)))
 			}
 			return n, nil
 		}
@@ -491,7 +491,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(int64(v)))
+		list.Append(protoreflect.ValueOfInt64(int64(v)))
 		return n, nil
 	case protoreflect.Fixed64Kind:
 		if wtyp == wire.BytesType {
@@ -505,7 +505,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(v))
+				list.Append(protoreflect.ValueOfUint64(v))
 			}
 			return n, nil
 		}
@@ -516,7 +516,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(v))
+		list.Append(protoreflect.ValueOfUint64(v))
 		return n, nil
 	case protoreflect.DoubleKind:
 		if wtyp == wire.BytesType {
@@ -530,7 +530,7 @@
 					return 0, wire.ParseError(n)
 				}
 				buf = buf[n:]
-				list.Append(protoreflect.ValueOf(math.Float64frombits(v)))
+				list.Append(protoreflect.ValueOfFloat64(math.Float64frombits(v)))
 			}
 			return n, nil
 		}
@@ -541,7 +541,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(math.Float64frombits(v)))
+		list.Append(protoreflect.ValueOfFloat64(math.Float64frombits(v)))
 		return n, nil
 	case protoreflect.StringKind:
 		if wtyp != wire.BytesType {
@@ -554,7 +554,7 @@
 		if strs.EnforceUTF8(fd) && !utf8.Valid(v) {
 			return 0, errors.InvalidUTF8(string(fd.FullName()))
 		}
-		list.Append(protoreflect.ValueOf(string(v)))
+		list.Append(protoreflect.ValueOfString(string(v)))
 		return n, nil
 	case protoreflect.BytesKind:
 		if wtyp != wire.BytesType {
@@ -564,7 +564,7 @@
 		if n < 0 {
 			return 0, wire.ParseError(n)
 		}
-		list.Append(protoreflect.ValueOf(append(([]byte)(nil), v...)))
+		list.Append(protoreflect.ValueOfBytes(append(([]byte)(nil), v...)))
 		return n, nil
 	case protoreflect.MessageKind:
 		if wtyp != wire.BytesType {
diff --git a/reflect/protoreflect/value_union.go b/reflect/protoreflect/value_union.go
index 0223d02..d695a4d 100644
--- a/reflect/protoreflect/value_union.go
+++ b/reflect/protoreflect/value_union.go
@@ -65,38 +65,101 @@
 	case nil:
 		return Value{}
 	case bool:
-		if v {
-			return Value{typ: boolType, num: 1}
-		} else {
-			return Value{typ: boolType, num: 0}
-		}
+		return ValueOfBool(v)
 	case int32:
-		return Value{typ: int32Type, num: uint64(v)}
+		return ValueOfInt32(v)
 	case int64:
-		return Value{typ: int64Type, num: uint64(v)}
+		return ValueOfInt64(v)
 	case uint32:
-		return Value{typ: uint32Type, num: uint64(v)}
+		return ValueOfUint32(v)
 	case uint64:
-		return Value{typ: uint64Type, num: uint64(v)}
+		return ValueOfUint64(v)
 	case float32:
-		return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
+		return ValueOfFloat32(v)
 	case float64:
-		return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
+		return ValueOfFloat64(v)
 	case string:
-		return valueOfString(v)
+		return ValueOfString(v)
 	case []byte:
-		return valueOfBytes(v[:len(v):len(v)])
+		return ValueOfBytes(v)
 	case EnumNumber:
-		return Value{typ: enumType, num: uint64(v)}
+		return ValueOfEnum(v)
 	case Message, List, Map:
 		return valueOfIface(v)
 	default:
-		// TODO: Special case Enum, ProtoMessage, *[]T, and *map[K]V?
-		// Note: this would violate the documented invariant in Interface.
 		panic(fmt.Sprintf("invalid type: %v", reflect.TypeOf(v)))
 	}
 }
 
+// ValueOfBool returns a new boolean value.
+func ValueOfBool(v bool) Value {
+	if v {
+		return Value{typ: boolType, num: 1}
+	} else {
+		return Value{typ: boolType, num: 0}
+	}
+}
+
+// ValueOfInt32 returns a new int32 value.
+func ValueOfInt32(v int32) Value {
+	return Value{typ: int32Type, num: uint64(v)}
+}
+
+// ValueOfInt64 returns a new int64 value.
+func ValueOfInt64(v int64) Value {
+	return Value{typ: int64Type, num: uint64(v)}
+}
+
+// ValueOfUint32 returns a new uint32 value.
+func ValueOfUint32(v uint32) Value {
+	return Value{typ: uint32Type, num: uint64(v)}
+}
+
+// ValueOfUint64 returns a new uint64 value.
+func ValueOfUint64(v uint64) Value {
+	return Value{typ: uint64Type, num: v}
+}
+
+// ValueOfFloat32 returns a new float32 value.
+func ValueOfFloat32(v float32) Value {
+	return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
+}
+
+// ValueOfFloat64 returns a new float64 value.
+func ValueOfFloat64(v float64) Value {
+	return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
+}
+
+// ValueOfString returns a new string value.
+func ValueOfString(v string) Value {
+	return valueOfString(v)
+}
+
+// ValueOfBytes returns a new bytes value.
+func ValueOfBytes(v []byte) Value {
+	return valueOfBytes(v[:len(v):len(v)])
+}
+
+// ValueOfEnum returns a new enum value.
+func ValueOfEnum(v EnumNumber) Value {
+	return Value{typ: enumType, num: uint64(v)}
+}
+
+// ValueOfMessage returns a new Message value.
+func ValueOfMessage(v Message) Value {
+	return valueOfIface(v)
+}
+
+// ValueOfList returns a new List value.
+func ValueOfList(v List) Value {
+	return valueOfIface(v)
+}
+
+// ValueOfMap returns a new Map value.
+func ValueOfMap(v Map) Value {
+	return valueOfIface(v)
+}
+
 // IsValid reports whether v is populated with a value.
 func (v Value) IsValid() bool {
 	return v.typ != nilType