in sdks/go/pkg/beam/core/typex/class.go [97:172]
func isConcrete(t reflect.Type, visited map[uintptr]bool) bool {
// Check that we haven't hit a recursive loop.
key := reflect.ValueOf(t).Pointer()
// If there's an invalid field in a recursive type
// then the layer above will find it.
if visited[key] {
return true
}
visited[key] = true
// Handle special types.
if t == nil ||
t == EventTimeType ||
t.Implements(WindowType) ||
t == reflectx.Error ||
t == reflectx.Context ||
IsUniversal(t) {
return false
}
switch t.Kind() {
case reflect.Invalid, reflect.UnsafePointer, reflect.Uintptr:
return false // no unmanageable types
case reflect.Chan, reflect.Func:
return false // no unserializable types
case reflect.Map:
return isConcrete(t.Elem(), visited) && isConcrete(t.Key(), visited)
case reflect.Array, reflect.Slice, reflect.Ptr:
return isConcrete(t.Elem(), visited)
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
// We ignore private fields under the assumption that they are
// either not needed or will be coded manually. For combiner
// accumulators, we need types that need non-trivial coding. Also,
// Go serialization schemes in general ignore private fields.
f := t.Field(i)
if len(f.Name) > 0 {
r, _ := utf8.DecodeRuneInString(f.Name)
if unicode.IsUpper(r) && !isConcrete(f.Type, visited) {
return false
}
}
}
return true
case reflect.Interface:
// Interface types must fail at construction time if no coder is registered for them.
return true
case reflect.Bool:
return true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.Complex64, reflect.Complex128:
return true
case reflect.String:
return true
default:
panic(fmt.Sprintf("Unexpected type kind: %v", t))
}
}