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]