Jelajahi Sumber

all: implement proto1 weak fields

This implements generation of and reflection support for weak fields.
Weak fields are a proto1 feature where the "weak" option can be specified
on a singular message field. A weak reference results in generated code
that does not directly link in the dependency containing the weak message.

Weak field support is not added to any of the serialization logic.

Change-Id: I08ccfa72bc80b2ffb6af527a1677a0a81dcf33fb
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185399
Reviewed-by: Damien Neil <dneil@google.com>
Joe Tsai 6 tahun lalu
induk
melakukan
3d8e369c4e

+ 49 - 2
cmd/protoc-gen-go/internal_gengo/main.go

@@ -403,6 +403,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 		g.P("state ", protoimplPackage.Ident("MessageState"))
 		sf.append("state")
 	}
+	hasWeak := false
 	for _, field := range message.Fields {
 		if field.Oneof != nil {
 			// It would be a bit simpler to iterate over the oneofs below,
@@ -443,8 +444,14 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 				fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)),
 			)
 		}
-		g.Annotate(message.GoIdent.GoName+"."+field.GoName, field.Location)
-		g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
+
+		name := field.GoName
+		if field.Desc.IsWeak() {
+			hasWeak = true
+			name = "XXX_weak_" + name
+		}
+		g.Annotate(message.GoIdent.GoName+"."+name, field.Location)
+		g.P(name, " ", goType, " `", strings.Join(tags, " "), "`",
 			deprecationComment(field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()))
 		sf.append(field.GoName)
 	}
@@ -460,6 +467,10 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 		g.P("sizeCache", " ", protoimplPackage.Ident("SizeCache"))
 		sf.append("sizeCache")
 	}
+	if hasWeak {
+		g.P("XXX_weak", " ", protoimplPackage.Ident("WeakFields"), " `json:\"-\"`")
+		sf.append("XXX_weak")
+	}
 	if generateExportedUnknownFields {
 		g.P("XXX_unrecognized", " ", protoimplPackage.Ident("UnknownFields"), " `json:\"-\"`")
 		sf.append("XXX_unrecognized")
@@ -591,6 +602,19 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 			g.P(deprecationComment(true))
 		}
 		g.Annotate(message.GoIdent.GoName+".Get"+field.GoName, field.Location)
+		if field.Desc.IsWeak() {
+			g.P("func (x *", message.GoIdent, ") Get", field.GoName, "() ", protoifacePackage.Ident("MessageV1"), "{")
+			g.P("if x != nil {")
+			g.P("v := x.XXX_weak[", field.Desc.Number(), "]")
+			g.P("_ = x.XXX_weak_" + field.GoName) // for field tracking
+			g.P("if v != nil {")
+			g.P("return v")
+			g.P("}")
+			g.P("}")
+			g.P("return ", protoimplPackage.Ident("X"), ".WeakNil(", strconv.Quote(string(field.Message.Desc.FullName())), ")")
+			g.P("}")
+			continue
+		}
 		g.P("func (x *", message.GoIdent, ") Get", field.GoName, "() ", goType, " {")
 		if field.Oneof != nil {
 			g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", fieldOneofType(field), "); ok {")
@@ -614,6 +638,25 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 		g.P()
 	}
 
+	// Setter methods.
+	for _, field := range message.Fields {
+		if field.Desc.IsWeak() {
+			g.Annotate(message.GoIdent.GoName+".Set"+field.GoName, field.Location)
+			g.P("func (x *", message.GoIdent, ") Set", field.GoName, "(v ", protoifacePackage.Ident("MessageV1"), ") {")
+			g.P("if x.XXX_weak == nil {")
+			g.P("x.XXX_weak = make(", protoimplPackage.Ident("WeakFields"), ")")
+			g.P("}")
+			g.P("if v == nil {")
+			g.P("delete(x.XXX_weak, ", field.Desc.Number(), ")")
+			g.P("} else {")
+			g.P("x.XXX_weak[", field.Desc.Number(), "] = v")
+			g.P("x.XXX_weak_"+field.GoName, " = struct{}{}") // for field tracking
+			g.P("}")
+			g.P("}")
+			g.P()
+		}
+	}
+
 	// Oneof wrapper types.
 	if len(message.Oneofs) > 0 {
 		genOneofWrappers(gen, g, f, message)
@@ -624,6 +667,10 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 //
 // If it returns pointer=true, the struct field is a pointer to the type.
 func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
+	if field.Desc.IsWeak() {
+		return "struct{}", false
+	}
+
 	pointer = true
 	switch field.Desc.Kind() {
 	case protoreflect.BoolKind:

+ 4 - 4
integration_test.go

@@ -48,8 +48,8 @@ func Test(t *testing.T) {
 
 	if *regenerate {
 		t.Run("Generate", func(t *testing.T) {
-			fmt.Print(mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-types", "-execute"))
-			fmt.Print(mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-protos", "-execute"))
+			fmt.Print(mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-types", "-execute"))
+			fmt.Print(mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-protos", "-execute"))
 			files := strings.Split(strings.TrimSpace(mustRunCommand(t, ".", "git", "ls-files", "*.go")), "\n")
 			mustRunCommand(t, ".", append([]string{"gofmt", "-w"}, files...)...)
 		})
@@ -94,11 +94,11 @@ func Test(t *testing.T) {
 		mustRunCommand(t, ".", runner, "--failure_list", failureList, "--enforce_recommended", driver)
 	})
 	t.Run("GeneratedGoFiles", func(t *testing.T) {
-		diff := mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-types")
+		diff := mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-types")
 		if strings.TrimSpace(diff) != "" {
 			t.Fatalf("stale generated files:\n%v", diff)
 		}
-		diff = mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-protos")
+		diff = mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-protos")
 		if strings.TrimSpace(diff) != "" {
 			t.Fatalf("stale generated files:\n%v", diff)
 		}

+ 3 - 3
internal/encoding/tag/tag.go

@@ -170,9 +170,6 @@ func Marshal(fd pref.FieldDescriptor, enumName string) string {
 	if fd.IsPacked() {
 		tag = append(tag, "packed")
 	}
-	if fd.IsWeak() {
-		tag = append(tag, "weak="+string(fd.Message().FullName()))
-	}
 	name := string(fd.Name())
 	if fd.Kind() == pref.GroupKind {
 		// The name of the FieldDescriptor for a group field is
@@ -186,6 +183,9 @@ func Marshal(fd pref.FieldDescriptor, enumName string) string {
 		// the exact same semantics from the previous generator.
 		tag = append(tag, "json="+jsonName)
 	}
+	if fd.IsWeak() {
+		tag = append(tag, "weak="+string(fd.Message().FullName()))
+	}
 	// The previous implementation does not tag extension fields as proto3,
 	// even when the field is defined in a proto3 file. Match that behavior
 	// for consistency.

+ 22 - 7
internal/filedesc/build_test.go

@@ -15,6 +15,7 @@ import (
 	"google.golang.org/protobuf/reflect/protoreflect"
 
 	testpb "google.golang.org/protobuf/internal/testprotos/test"
+	_ "google.golang.org/protobuf/internal/testprotos/test/weak1"
 	"google.golang.org/protobuf/types/descriptorpb"
 )
 
@@ -104,14 +105,28 @@ func visitFields(m protoreflect.Message, f func(protoreflect.FieldDescriptor)) {
 
 func TestWeakInit(t *testing.T) {
 	file := testpb.File_test_test_proto
-	fd := file.Messages().ByName("TestWeak").Fields().ByName("weak_message")
-	if want, got := fd.IsWeak(), true; got != want {
-		t.Errorf("field %v: IsWeak() = %v, want %v", fd.FullName(), want, got)
+
+	// We do not expect to get a placeholder since weak1 is imported.
+	fd1 := file.Messages().ByName("TestWeak").Fields().ByName("weak_message1")
+	if got, want := fd1.IsWeak(), true; got != want {
+		t.Errorf("field %v: IsWeak() = %v, want %v", fd1.FullName(), got, want)
+	}
+	if got, want := fd1.Message().IsPlaceholder(), false; got != want {
+		t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd1.FullName(), got, want)
+	}
+	if got, want := fd1.Message().Fields().Len(), 1; got != want {
+		t.Errorf("field %v: Message().Fields().Len() == %d, want %d", fd1.FullName(), got, want)
+	}
+
+	// We do expect to get a placeholder since weak2 is not imported.
+	fd2 := file.Messages().ByName("TestWeak").Fields().ByName("weak_message2")
+	if got, want := fd2.IsWeak(), true; got != want {
+		t.Errorf("field %v: IsWeak() = %v, want %v", fd2.FullName(), got, want)
 	}
-	if want, got := fd.Message().IsPlaceholder(), false; got != want {
-		t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd.FullName(), want, got)
+	if got, want := fd2.Message().IsPlaceholder(), true; got != want {
+		t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd2.FullName(), got, want)
 	}
-	if fd.Message().Fields().Len() == 0 {
-		t.Errorf("field %v: Message().Fields().Len() == 0, want >0", fd.FullName())
+	if got, want := fd2.Message().Fields().Len(), 0; got != want {
+		t.Errorf("field %v: Message().Fields().Len() == %d, want %d", fd2.FullName(), got, want)
 	}
 }

+ 14 - 0
internal/impl/legacy_export.go

@@ -7,11 +7,15 @@ package impl
 import (
 	"encoding/binary"
 	"encoding/json"
+	"fmt"
 	"hash/crc32"
 	"math"
+	"reflect"
 
 	"google.golang.org/protobuf/internal/errors"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
+	piface "google.golang.org/protobuf/runtime/protoiface"
 )
 
 // These functions exist to support exported APIs in generated protobufs.
@@ -74,3 +78,13 @@ func (Export) CompressGZIP(in []byte) (out []byte) {
 	out = append(out, gzipFooter[:]...)
 	return out
 }
+
+// WeakNil returns a typed nil pointer to a concrete message.
+// It panics if the message is not linked into the binary.
+func (Export) WeakNil(s pref.FullName) piface.MessageV1 {
+	mt, err := protoregistry.GlobalTypes.FindMessageByName(s)
+	if err == nil {
+		panic(fmt.Sprintf("weak message %v is not linked in", s))
+	}
+	return reflect.Zero(mt.GoType()).Interface().(piface.MessageV1)
+}

+ 9 - 0
internal/impl/message.go

@@ -120,18 +120,21 @@ func (mi *MessageInfo) initOnce() {
 
 type (
 	SizeCache       = int32
+	WeakFields      = map[int32]piface.MessageV1
 	UnknownFields   = []byte
 	ExtensionFields = map[int32]ExtensionField
 )
 
 var (
 	sizecacheType       = reflect.TypeOf(SizeCache(0))
+	weakFieldsType      = reflect.TypeOf(WeakFields(nil))
 	unknownFieldsType   = reflect.TypeOf(UnknownFields(nil))
 	extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
 )
 
 type structInfo struct {
 	sizecacheOffset offset
+	weakOffset      offset
 	unknownOffset   offset
 	extensionOffset offset
 
@@ -144,6 +147,7 @@ type structInfo struct {
 func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
 	si := structInfo{
 		sizecacheOffset: invalidOffset,
+		weakOffset:      invalidOffset,
 		unknownOffset:   invalidOffset,
 		extensionOffset: invalidOffset,
 
@@ -159,6 +163,9 @@ func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
 	if f, _ := t.FieldByName("XXX_sizecache"); f.Type == sizecacheType {
 		si.sizecacheOffset = offsetOf(f, mi.Exporter)
 	}
+	if f, _ := t.FieldByName("XXX_weak"); f.Type == weakFieldsType {
+		si.weakOffset = offsetOf(f, mi.Exporter)
+	}
 	if f, _ := t.FieldByName("unknownFields"); f.Type == unknownFieldsType {
 		si.unknownOffset = offsetOf(f, mi.Exporter)
 	}
@@ -235,6 +242,8 @@ func (mi *MessageInfo) makeKnownFieldsFunc(si structInfo) {
 			fi = fieldInfoForMap(fd, fs, mi.Exporter)
 		case fd.IsList():
 			fi = fieldInfoForList(fd, fs, mi.Exporter)
+		case fd.IsWeak():
+			fi = fieldInfoForWeakMessage(fd, si.weakOffset)
 		case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
 			fi = fieldInfoForMessage(fd, fs, mi.Exporter)
 		default:

+ 85 - 0
internal/impl/message_field.go

@@ -9,8 +9,10 @@ import (
 	"math"
 	"reflect"
 
+	"google.golang.org/protobuf/internal/flags"
 	pvalue "google.golang.org/protobuf/internal/value"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	preg "google.golang.org/protobuf/reflect/protoregistry"
 	piface "google.golang.org/protobuf/runtime/protoiface"
 )
 
@@ -289,6 +291,89 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField, x expor
 	}
 }
 
+func fieldInfoForWeakMessage(fd pref.FieldDescriptor, weakOffset offset) fieldInfo {
+	if !flags.Proto1Legacy {
+		panic("no support for proto1 weak fields")
+	}
+
+	messageName := fd.Message().FullName()
+	messageType, _ := preg.GlobalTypes.FindMessageByName(messageName)
+	if messageType == nil {
+		return fieldInfo{
+			fieldDesc: fd,
+			has:       func(p pointer) bool { return false },
+			clear:     func(p pointer) {},
+			get: func(p pointer) pref.Value {
+				panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+			},
+			set: func(p pointer, v pref.Value) {
+				panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+			},
+			mutable: func(p pointer) pref.Value {
+				panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+			},
+			newMessage: func() pref.Message {
+				panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+			},
+		}
+	}
+
+	num := int32(fd.Number())
+	frozenEmpty := pref.ValueOf(frozenMessage{messageType.New()})
+	return fieldInfo{
+		fieldDesc: fd,
+		has: func(p pointer) bool {
+			if p.IsNil() {
+				return false
+			}
+			fs := p.Apply(weakOffset).WeakFields()
+			_, ok := (*fs)[num]
+			return ok
+		},
+		clear: func(p pointer) {
+			fs := p.Apply(weakOffset).WeakFields()
+			delete(*fs, num)
+		},
+		get: func(p pointer) pref.Value {
+			if p.IsNil() {
+				return frozenEmpty
+			}
+			fs := p.Apply(weakOffset).WeakFields()
+			m, ok := (*fs)[num]
+			if !ok {
+				return frozenEmpty
+			}
+			return pref.ValueOf(m.(pref.ProtoMessage).ProtoReflect())
+		},
+		set: func(p pointer, v pref.Value) {
+			m := v.Message()
+			if m.Descriptor() != messageType.Descriptor() {
+				panic("mismatching message descriptor")
+			}
+			fs := p.Apply(weakOffset).WeakFields()
+			if *fs == nil {
+				*fs = make(WeakFields)
+			}
+			(*fs)[num] = m.Interface().(piface.MessageV1)
+		},
+		mutable: func(p pointer) pref.Value {
+			fs := p.Apply(weakOffset).WeakFields()
+			if *fs == nil {
+				*fs = make(WeakFields)
+			}
+			m, ok := (*fs)[num]
+			if !ok {
+				m = messageType.New().Interface().(piface.MessageV1)
+				(*fs)[num] = m
+			}
+			return pref.ValueOf(m.(pref.ProtoMessage).ProtoReflect())
+		},
+		newMessage: func() pref.Message {
+			return messageType.New()
+		},
+	}
+}
+
 func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
 	ft := fs.Type
 	conv, _ := newConverter(ft, fd.Kind())

+ 1 - 0
internal/impl/pointer_reflect.go

@@ -122,6 +122,7 @@ func (p pointer) StringPtr() **string      { return p.v.Interface().(**string) }
 func (p pointer) StringSlice() *[]string   { return p.v.Interface().(*[]string) }
 func (p pointer) Bytes() *[]byte           { return p.v.Interface().(*[]byte) }
 func (p pointer) BytesSlice() *[][]byte    { return p.v.Interface().(*[][]byte) }
+func (p pointer) WeakFields() *WeakFields  { return p.v.Interface().(*WeakFields) }
 func (p pointer) Extensions() *map[int32]ExtensionField {
 	return p.v.Interface().(*map[int32]ExtensionField)
 }

+ 1 - 0
internal/impl/pointer_unsafe.go

@@ -110,6 +110,7 @@ func (p pointer) StringPtr() **string                   { return (**string)(p.p)
 func (p pointer) StringSlice() *[]string                { return (*[]string)(p.p) }
 func (p pointer) Bytes() *[]byte                        { return (*[]byte)(p.p) }
 func (p pointer) BytesSlice() *[][]byte                 { return (*[][]byte)(p.p) }
+func (p pointer) WeakFields() *WeakFields               { return (*WeakFields)(p.p) }
 func (p pointer) Extensions() *map[int32]ExtensionField { return (*map[int32]ExtensionField)(p.p) }
 
 func (p pointer) Elem() pointer {

File diff ditekan karena terlalu besar
+ 1139 - 1093
internal/testprotos/test/test.pb.go


+ 4 - 2
internal/testprotos/test/test.proto

@@ -8,7 +8,8 @@ package goproto.proto.test;
 
 import "test/test_import.proto";
 import public "test/test_public.proto";
-import weak "test/weak/test_weak.proto";
+import weak "test/weak1/test_weak.proto";
+import weak "test/weak2/test_weak.proto";
 
 option go_package = "google.golang.org/protobuf/internal/testprotos/test";
 
@@ -262,7 +263,8 @@ message TestRequiredGroupFields {
 }
 
 message TestWeak {
-  optional goproto.proto.test.weak.WeakImportMessage weak_message = 1 [weak=true];
+  optional goproto.proto.test.weak.WeakImportMessage1 weak_message1 = 1 [weak=true];
+  optional goproto.proto.test.weak.WeakImportMessage2 weak_message2 = 2 [weak=true];
 }
 
 message TestPackedTypes {

+ 134 - 0
internal/testprotos/test/weak1/test_weak.pb.go

@@ -0,0 +1,134 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: test/weak1/test_weak.proto
+
+package weak1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	sync "sync"
+)
+
+const (
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 0)
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(0 - protoimpl.MinVersion)
+)
+
+type WeakImportMessage1 struct {
+	state         protoimpl.MessageState
+	A             *int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *WeakImportMessage1) Reset() {
+	*x = WeakImportMessage1{}
+}
+
+func (x *WeakImportMessage1) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WeakImportMessage1) ProtoMessage() {}
+
+func (x *WeakImportMessage1) ProtoReflect() protoreflect.Message {
+	mi := &file_test_weak1_test_weak_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use WeakImportMessage1.ProtoReflect.Type instead.
+func (*WeakImportMessage1) Descriptor() ([]byte, []int) {
+	return file_test_weak1_test_weak_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *WeakImportMessage1) GetA() int32 {
+	if x != nil && x.A != nil {
+		return *x.A
+	}
+	return 0
+}
+
+var File_test_weak1_test_weak_proto protoreflect.FileDescriptor
+
+var file_test_weak1_test_weak_proto_rawDesc = []byte{
+	0x0a, 0x1a, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31, 0x2f, 0x74, 0x65, 0x73,
+	0x74, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74,
+	0x2e, 0x77, 0x65, 0x61, 0x6b, 0x22, 0x22, 0x0a, 0x12, 0x57, 0x65, 0x61, 0x6b, 0x49, 0x6d, 0x70,
+	0x6f, 0x72, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x31, 0x12, 0x0c, 0x0a, 0x01, 0x61,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x61, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+	0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74,
+	0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31,
+}
+
+var (
+	file_test_weak1_test_weak_proto_rawDescOnce sync.Once
+	file_test_weak1_test_weak_proto_rawDescData = file_test_weak1_test_weak_proto_rawDesc
+)
+
+func file_test_weak1_test_weak_proto_rawDescGZIP() []byte {
+	file_test_weak1_test_weak_proto_rawDescOnce.Do(func() {
+		file_test_weak1_test_weak_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_weak1_test_weak_proto_rawDescData)
+	})
+	return file_test_weak1_test_weak_proto_rawDescData
+}
+
+var file_test_weak1_test_weak_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_test_weak1_test_weak_proto_goTypes = []interface{}{
+	(*WeakImportMessage1)(nil), // 0: goproto.proto.test.weak.WeakImportMessage1
+}
+var file_test_weak1_test_weak_proto_depIdxs = []int32{
+	0, // starting offset of method output_type sub-list
+	0, // starting offset of method input_type sub-list
+	0, // starting offset of extension type_name sub-list
+	0, // starting offset of extension extendee sub-list
+	0, // starting offset of field type_name sub-list
+}
+
+func init() { file_test_weak1_test_weak_proto_init() }
+func file_test_weak1_test_weak_proto_init() {
+	if File_test_weak1_test_weak_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_test_weak1_test_weak_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*WeakImportMessage1); i {
+			case 0:
+				return &v.state
+			case 2:
+				return &v.sizeCache
+			case 3:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			RawDescriptor: file_test_weak1_test_weak_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_test_weak1_test_weak_proto_goTypes,
+		DependencyIndexes: file_test_weak1_test_weak_proto_depIdxs,
+		MessageInfos:      file_test_weak1_test_weak_proto_msgTypes,
+	}.Build()
+	File_test_weak1_test_weak_proto = out.File
+	file_test_weak1_test_weak_proto_rawDesc = nil
+	file_test_weak1_test_weak_proto_goTypes = nil
+	file_test_weak1_test_weak_proto_depIdxs = nil
+}

+ 2 - 2
internal/testprotos/test/weak/test_weak.proto → internal/testprotos/test/weak1/test_weak.proto

@@ -6,8 +6,8 @@ syntax = "proto2";
 
 package goproto.proto.test.weak;
 
-option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak";
+option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak1";
 
-message WeakImportMessage {
+message WeakImportMessage1 {
 	optional int32 a = 1;
 }

+ 134 - 0
internal/testprotos/test/weak2/test_weak.pb.go

@@ -0,0 +1,134 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: test/weak2/test_weak.proto
+
+package weak1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	sync "sync"
+)
+
+const (
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 0)
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(0 - protoimpl.MinVersion)
+)
+
+type WeakImportMessage2 struct {
+	state         protoimpl.MessageState
+	A             *int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *WeakImportMessage2) Reset() {
+	*x = WeakImportMessage2{}
+}
+
+func (x *WeakImportMessage2) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WeakImportMessage2) ProtoMessage() {}
+
+func (x *WeakImportMessage2) ProtoReflect() protoreflect.Message {
+	mi := &file_test_weak2_test_weak_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use WeakImportMessage2.ProtoReflect.Type instead.
+func (*WeakImportMessage2) Descriptor() ([]byte, []int) {
+	return file_test_weak2_test_weak_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *WeakImportMessage2) GetA() int32 {
+	if x != nil && x.A != nil {
+		return *x.A
+	}
+	return 0
+}
+
+var File_test_weak2_test_weak_proto protoreflect.FileDescriptor
+
+var file_test_weak2_test_weak_proto_rawDesc = []byte{
+	0x0a, 0x1a, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x77, 0x65, 0x61, 0x6b, 0x32, 0x2f, 0x74, 0x65, 0x73,
+	0x74, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74,
+	0x2e, 0x77, 0x65, 0x61, 0x6b, 0x22, 0x22, 0x0a, 0x12, 0x57, 0x65, 0x61, 0x6b, 0x49, 0x6d, 0x70,
+	0x6f, 0x72, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x12, 0x0c, 0x0a, 0x01, 0x61,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x61, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+	0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74,
+	0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31,
+}
+
+var (
+	file_test_weak2_test_weak_proto_rawDescOnce sync.Once
+	file_test_weak2_test_weak_proto_rawDescData = file_test_weak2_test_weak_proto_rawDesc
+)
+
+func file_test_weak2_test_weak_proto_rawDescGZIP() []byte {
+	file_test_weak2_test_weak_proto_rawDescOnce.Do(func() {
+		file_test_weak2_test_weak_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_weak2_test_weak_proto_rawDescData)
+	})
+	return file_test_weak2_test_weak_proto_rawDescData
+}
+
+var file_test_weak2_test_weak_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_test_weak2_test_weak_proto_goTypes = []interface{}{
+	(*WeakImportMessage2)(nil), // 0: goproto.proto.test.weak.WeakImportMessage2
+}
+var file_test_weak2_test_weak_proto_depIdxs = []int32{
+	0, // starting offset of method output_type sub-list
+	0, // starting offset of method input_type sub-list
+	0, // starting offset of extension type_name sub-list
+	0, // starting offset of extension extendee sub-list
+	0, // starting offset of field type_name sub-list
+}
+
+func init() { file_test_weak2_test_weak_proto_init() }
+func file_test_weak2_test_weak_proto_init() {
+	if File_test_weak2_test_weak_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_test_weak2_test_weak_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*WeakImportMessage2); i {
+			case 0:
+				return &v.state
+			case 2:
+				return &v.sizeCache
+			case 3:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			RawDescriptor: file_test_weak2_test_weak_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_test_weak2_test_weak_proto_goTypes,
+		DependencyIndexes: file_test_weak2_test_weak_proto_depIdxs,
+		MessageInfos:      file_test_weak2_test_weak_proto_msgTypes,
+	}.Build()
+	File_test_weak2_test_weak_proto = out.File
+	file_test_weak2_test_weak_proto_rawDesc = nil
+	file_test_weak2_test_weak_proto_goTypes = nil
+	file_test_weak2_test_weak_proto_depIdxs = nil
+}

+ 13 - 0
internal/testprotos/test/weak2/test_weak.proto

@@ -0,0 +1,13 @@
+// Copyright 2019 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.
+
+syntax = "proto2";
+
+package goproto.proto.test.weak;
+
+option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak1";
+
+message WeakImportMessage2 {
+	optional int32 a = 1;
+}

+ 8 - 1
reflect/protodesc/desc_validate.go

@@ -11,6 +11,7 @@ import (
 	"google.golang.org/protobuf/internal/encoding/wire"
 	"google.golang.org/protobuf/internal/errors"
 	"google.golang.org/protobuf/internal/filedesc"
+	"google.golang.org/protobuf/internal/flags"
 	"google.golang.org/protobuf/internal/strs"
 	"google.golang.org/protobuf/reflect/protoreflect"
 
@@ -102,6 +103,9 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc
 				return errors.New("message %q has conflicting fields: %q with %q", m.FullName(), f1.Name(), f2.Name())
 			}
 		}
+		if isMessageSet && !flags.Proto1Legacy {
+			return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName())
+		}
 		if isMessageSet && (m.Syntax() != protoreflect.Proto2 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) {
 			return errors.New("message %q is an invalid proto1 MessageSet", m.FullName())
 		}
@@ -143,7 +147,10 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc
 			if fd.Extendee != nil {
 				return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee())
 			}
-			if f.IsWeak() && (!isOptionalMessage(f) || f.ContainingOneof() != nil) {
+			if f.IsWeak() && !flags.Proto1Legacy {
+				return errors.New("message field %q is a weak field, which is a legacy proto1 feature that is no longer supported", f.FullName())
+			}
+			if f.IsWeak() && (f.Syntax() != protoreflect.Proto2 || !isOptionalMessage(f) || f.ContainingOneof() != nil) {
 				return errors.New("message field %q may only be weak for an optional message", f.FullName())
 			}
 			if f.IsPacked() && !isPackable(f) {

+ 15 - 1
reflect/protodesc/file_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/internal/flags"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoregistry"
 
@@ -767,7 +768,13 @@ func TestNewFile(t *testing.T) {
 				options: {message_set_wire_format:true}
 			}]}]
 		`),
-		wantErr: `message "M.M" is an invalid proto1 MessageSet`,
+		wantErr: func() string {
+			if flags.Proto1Legacy {
+				return `message "M.M" is an invalid proto1 MessageSet`
+			} else {
+				return `message "M.M" is a MessageSet, which is a legacy proto1 feature that is no longer supported`
+			}
+		}(),
 	}, {
 		label: "valid MessageSet",
 		inDesc: mustParseFile(`
@@ -779,6 +786,13 @@ func TestNewFile(t *testing.T) {
 				options:         {message_set_wire_format:true}
 			}]}]
 		`),
+		wantErr: func() string {
+			if flags.Proto1Legacy {
+				return ""
+			} else {
+				return `message "M.M" is a MessageSet, which is a legacy proto1 feature that is no longer supported`
+			}
+		}(),
 	}, {
 		label: "invalid extension ranges in proto3",
 		inDesc: mustParseFile(`

+ 1 - 0
runtime/protoimpl/impl.go

@@ -68,6 +68,7 @@ type (
 	MessageInfo      = impl.MessageInfo
 	MessageState     = impl.MessageState
 	SizeCache        = impl.SizeCache
+	WeakFields       = impl.WeakFields
 	UnknownFields    = impl.UnknownFields
 	ExtensionFields  = impl.ExtensionFields
 	ExtensionFieldV1 = impl.ExtensionField

+ 13 - 5
testing/prototest/prototest_test.go

@@ -8,22 +8,30 @@ import (
 	"fmt"
 	"testing"
 
+	"google.golang.org/protobuf/internal/flags"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/prototest"
+
 	irregularpb "google.golang.org/protobuf/internal/testprotos/irregular"
 	testpb "google.golang.org/protobuf/internal/testprotos/test"
+	_ "google.golang.org/protobuf/internal/testprotos/test/weak1"
+	_ "google.golang.org/protobuf/internal/testprotos/test/weak2"
 	test3pb "google.golang.org/protobuf/internal/testprotos/test3"
-	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/testing/prototest"
 )
 
 func Test(t *testing.T) {
-	for _, m := range []proto.Message{
+	ms := []proto.Message{
 		(*testpb.TestAllTypes)(nil),
 		(*test3pb.TestAllTypes)(nil),
 		(*testpb.TestRequired)(nil),
-		(*testpb.TestWeak)(nil),
 		(*irregularpb.Message)(nil),
 		(*testpb.TestAllExtensions)(nil),
-	} {
+	}
+	if flags.Proto1Legacy {
+		ms = append(ms, (*testpb.TestWeak)(nil))
+	}
+
+	for _, m := range ms {
 		t.Run(fmt.Sprintf("%T", m), func(t *testing.T) {
 			prototest.TestMessage(t, m, prototest.MessageOptions{})
 		})

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini