@@ -79,23 +79,15 @@ func Fetch(from, i any) any {
7979 if cv , ok := fieldCache .Load (key ); ok {
8080 return v .FieldByIndex (cv .([]int )).Interface ()
8181 }
82- field , ok := t .FieldByNameFunc (func (name string ) bool {
83- field , _ := t .FieldByName (name )
84- switch field .Tag .Get ("expr" ) {
85- case "-" :
86- return false
87- case fieldName :
88- return true
89- default :
90- return name == fieldName
91- }
92- })
93- if ok && field .IsExported () {
94- value := v .FieldByIndex (field .Index )
95- if value .IsValid () {
96- fieldCache .Store (key , field .Index )
97- return value .Interface ()
98- }
82+ if value , field , ok := findStructField (v , fieldName ); ok {
83+ fieldCache .Store (key , field .Index )
84+ return value .Interface ()
85+ }
86+ // Field isn't found via standard Go promotion. Try to find it
87+ // by traversing embedded interface values whose concrete types
88+ // may contain the requested field.
89+ if result , found := fetchFromEmbeddedInterfaces (v , fieldName ); found {
90+ return result
9991 }
10092 }
10193 panic (fmt .Sprintf ("cannot fetch %v from %T" , i , from ))
@@ -143,6 +135,82 @@ func fieldByIndex(v reflect.Value, field *Field) reflect.Value {
143135 return v
144136}
145137
138+ func findStructField (v reflect.Value , fieldName string ) (reflect.Value , reflect.StructField , bool ) {
139+ t := v .Type ()
140+ field , ok := t .FieldByNameFunc (func (name string ) bool {
141+ sf , _ := t .FieldByName (name )
142+ switch sf .Tag .Get ("expr" ) {
143+ case "-" :
144+ return false
145+ case fieldName :
146+ return true
147+ default :
148+ return name == fieldName
149+ }
150+ })
151+ if ok && field .IsExported () {
152+ value := v .FieldByIndex (field .Index )
153+ if value .IsValid () {
154+ return value , field , true
155+ }
156+ }
157+ return reflect.Value {}, reflect.StructField {}, false
158+ }
159+
160+ func fetchFromEmbeddedInterfaces (v reflect.Value , fieldName string ) (any , bool ) {
161+ t := v .Type ()
162+ for i := 0 ; i < t .NumField (); i ++ {
163+ f := t .Field (i )
164+ if ! f .Anonymous {
165+ continue
166+ }
167+ fv := v .Field (i )
168+ fk := f .Type .Kind ()
169+
170+ // Dereference pointers to get to the underlying type.
171+ for fk == reflect .Ptr {
172+ if fv .IsNil () {
173+ break
174+ }
175+ fv = fv .Elem ()
176+ fk = fv .Kind ()
177+ }
178+
179+ switch fk {
180+ case reflect .Interface :
181+ if fv .IsNil () {
182+ continue
183+ }
184+ // Unwrap interface and dereference pointers to reach the
185+ // concrete struct value.
186+ concrete := fv .Elem ()
187+ for concrete .Kind () == reflect .Ptr {
188+ if concrete .IsNil () {
189+ break
190+ }
191+ concrete = concrete .Elem ()
192+ }
193+ if concrete .Kind () != reflect .Struct {
194+ continue
195+ }
196+ if value , _ , ok := findStructField (concrete , fieldName ); ok {
197+ return value .Interface (), true
198+ }
199+ // The concrete type itself may have embedded interfaces.
200+ if result , found := fetchFromEmbeddedInterfaces (concrete , fieldName ); found {
201+ return result , found
202+ }
203+
204+ case reflect .Struct :
205+ // Recurse into embedded structs to find embedded interfaces.
206+ if result , found := fetchFromEmbeddedInterfaces (fv , fieldName ); found {
207+ return result , found
208+ }
209+ }
210+ }
211+ return nil , false
212+ }
213+
146214type Method struct {
147215 Index int
148216 Name string
0 commit comments