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:
[email protected]


Reply via email to