You are viewing a plain text version of this content. The canonical link for it is here.
Posted to github@beam.apache.org by GitBox <gi...@apache.org> on 2021/03/17 16:21:52 UTC

[GitHub] [beam] lostluck commented on a change in pull request #14192: [BEAM-9615] Embedded structs and Deterministic map encoding, and Logical Types.

lostluck commented on a change in pull request #14192:
URL: https://github.com/apache/beam/pull/14192#discussion_r596180354



##########
File path: sdks/go/pkg/beam/core/graph/coder/row_decoder.go
##########
@@ -114,16 +114,38 @@ func (b *RowDecoderBuilder) decoderForType(t reflect.Type) (func(io.Reader) (int
 // decoderForStructReflect returns a reflection based decoder function for the
 // given struct type.
 func (b *RowDecoderBuilder) decoderForStructReflect(t reflect.Type) (func(reflect.Value, io.Reader) error, error) {
-
 	var coder typeDecoderReflect
 	coder.typ = t
 	for i := 0; i < t.NumField(); i++ {
 		i := i // avoid alias issues in the closures.
 		sf := t.Field(i)
 		isUnexported := sf.PkgPath != ""
-		if isUnexported {
+		if sf.Anonymous {
+			ft := sf.Type
+			if ft.Kind() == reflect.Ptr {
+				// If a struct embeds a pointer to an unexported type,
+				// it is not possible to set a newly allocated value
+				// since the field is unexported.
+				//
+				// See https://golang.org/issue/21357
+				//
+				// Since the values are created by this package reflectively,
+				// there's no work around like pre-allocating the field
+				// manually.
+				if isUnexported {

Review comment:
       This one is pretty complicated.
   A reflect.StructField.PkgPath has this documentation
   `PkgPath is the package path that qualifies a lower case (unexported) field name. It is empty for upper case (exported) field names.` So normally, it's only referring to the field itself.
   This changes when it's an embedded field. As far as the AST (abstract syntax tree) and reflective representations are concerned,  an embedded field is named *the same as the type being embedded*. However, this means that if you're embedding an unexported type, that has Exported fields itself.  So in the block where we check whether the field is Anonymous (AKA embedded), we know that the exported state of the field indicates whether the embedded type is unexported.
   But, you want to be able to access those fields, and methods. The compiler hoists the methods on embedded fields to the containing type, allowing the any interfaces the embedded type satisfieds to be also satisfied by the containing type. So, in this case, we want to serialize the exported fields of the embedded type, whether or not the type itself is exported, since the user expectation is they could access it.
   
   Relatedly, if the embedded type is a pointer to an unexported type, there's no way for us to synthetically create and allocate to that field, hence the big comment there. A non-pointer doesn't have this problem because the fields of the embedded type are a part of the container type's allocation, so we have access to them as expected. The Go reflect library desperately avoids allocating and changing values of unexported fields, but that's not the same on types themselves.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org