Source file src/simd/archsimd/_gen/unify/reflect.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package unify
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"unicode"
    14  )
    15  
    16  // Decode decodes v into a Go value.
    17  //
    18  // v must be exact, except that it can include Top. into must be a pointer.
    19  // [Def]s are decoded into structs. [Tuple]s are decoded into slices. [String]s
    20  // are decoded into strings or ints. Any field can itself be a pointer to one of
    21  // these types. Top can be decoded into a pointer-typed field and will set the
    22  // field to nil. Anything else will allocate a value if necessary.
    23  //
    24  // Any type may implement [Decoder], in which case its DecodeUnified method will
    25  // be called instead of using the default decoding scheme.
    26  func (v *Value) Decode(into any) error {
    27  	rv := reflect.ValueOf(into)
    28  	if rv.Kind() != reflect.Pointer {
    29  		return fmt.Errorf("cannot decode into non-pointer %T", into)
    30  	}
    31  	return decodeReflect(v, rv.Elem())
    32  }
    33  
    34  // Decoder can be implemented by types as a custom implementation of [Decode]
    35  // for that type.
    36  type Decoder interface {
    37  	DecodeUnified(v *Value) error
    38  }
    39  
    40  var decoderType = reflect.TypeFor[Decoder]()
    41  
    42  func decodeReflect(v *Value, rv reflect.Value) error {
    43  	var ptr reflect.Value
    44  	if rv.Kind() == reflect.Pointer {
    45  		if rv.IsNil() {
    46  			// Transparently allocate through pointers, *except* for Top, which
    47  			// wants to set the pointer to nil.
    48  			//
    49  			// TODO: Drop this condition if I switch to an explicit Optional[T]
    50  			// or move the Top logic into Def.
    51  			if _, ok := v.Domain.(Top); !ok {
    52  				// Allocate the value to fill in, but don't actually store it in
    53  				// the pointer until we successfully decode.
    54  				ptr = rv
    55  				rv = reflect.New(rv.Type().Elem()).Elem()
    56  			}
    57  		} else {
    58  			rv = rv.Elem()
    59  		}
    60  	}
    61  
    62  	var err error
    63  	if reflect.PointerTo(rv.Type()).Implements(decoderType) {
    64  		// Use the custom decoder.
    65  		err = rv.Addr().Interface().(Decoder).DecodeUnified(v)
    66  	} else {
    67  		err = v.Domain.decode(rv)
    68  	}
    69  	if err == nil && ptr.IsValid() {
    70  		ptr.Set(rv.Addr())
    71  	}
    72  	return err
    73  }
    74  
    75  type inexactError struct {
    76  	valueType string
    77  	goType    string
    78  }
    79  
    80  func (e *inexactError) Error() string {
    81  	return fmt.Sprintf("cannot store inexact %s value in %s", e.valueType, e.goType)
    82  }
    83  
    84  type decodeError struct {
    85  	path string
    86  	err  error
    87  }
    88  
    89  func newDecodeError(path string, err error) *decodeError {
    90  	if err, ok := err.(*decodeError); ok {
    91  		return &decodeError{path: path + "." + err.path, err: err.err}
    92  	}
    93  	return &decodeError{path: path, err: err}
    94  }
    95  
    96  func (e *decodeError) Unwrap() error {
    97  	return e.err
    98  }
    99  
   100  func (e *decodeError) Error() string {
   101  	return fmt.Sprintf("%s: %s", e.path, e.err)
   102  }
   103  
   104  func (d Var) decode(rv reflect.Value) error {
   105  	return &inexactError{"var", rv.Type().String()}
   106  }
   107  
   108  func (t Top) decode(rv reflect.Value) error {
   109  	// We can decode Top into a pointer-typed value as nil.
   110  	if rv.Kind() != reflect.Pointer {
   111  		return &inexactError{"top", rv.Type().String()}
   112  	}
   113  	rv.SetZero()
   114  	return nil
   115  }
   116  
   117  func (d Def) decode(rv reflect.Value) error {
   118  	if rv.Kind() != reflect.Struct {
   119  		return fmt.Errorf("cannot decode Def into %s", rv.Type())
   120  	}
   121  
   122  	fieldMap := canonStructFields(rv.Type())
   123  	for defName, f := range fieldMap {
   124  		v := d.fields[defName]
   125  		if v == nil {
   126  			v = topValue
   127  		}
   128  		if err := decodeReflect(v, rv.FieldByIndex(f.Index)); err != nil {
   129  			return newDecodeError(f.Name, err)
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  var structFieldsCache sync.Map /*[reflect.Type, map[string]reflect.StructField]*/
   136  
   137  // canonStructFields canonicalizes the name of all exported fields in rt to from
   138  // Go-style exported names to YAML-style lower-case names. If a name starts with
   139  // N upper-case letters, then if N==1, it lower-cases just the first letter; if
   140  // N=len, it lower-cases the whole name; otherwise it lower-cases the first N-1
   141  // letters.
   142  //
   143  // For example:
   144  //
   145  //	AsmPos      => asmPos
   146  //	CPUFeatures => cpuFeatures
   147  //	GOARCH      => goarch
   148  //
   149  // It returns a map from Def field name to struct field. The mapping between Go
   150  // field names and Def names is a bijection, so it can be used for encoding and
   151  // decoding.
   152  //
   153  // rt must be a struct type.
   154  func canonStructFields(rt reflect.Type) map[string]reflect.StructField {
   155  	type fieldMap = map[string]reflect.StructField
   156  	if fields, ok := structFieldsCache.Load(rt); ok {
   157  		return fields.(fieldMap)
   158  	}
   159  
   160  	fm := make(fieldMap)
   161  	for f := range rt.Fields() {
   162  		if !f.IsExported() {
   163  			continue
   164  		}
   165  		defName := lowerGoName(f.Name)
   166  		if _, ok := fm[defName]; ok {
   167  			panic(fmt.Sprintf("multiple fields in type %s map to %q", rt, defName))
   168  		}
   169  		fm[defName] = f
   170  	}
   171  
   172  	res, _ := structFieldsCache.LoadOrStore(rt, fm)
   173  	return res.(fieldMap)
   174  }
   175  
   176  func lowerGoName(goName string) string {
   177  	prefixBytes := -1
   178  	prevBytes := 0
   179  	allUpper := true
   180  	for pos, ch := range goName {
   181  		if !unicode.IsUpper(ch) {
   182  			allUpper = false
   183  			prefixBytes = pos
   184  			break
   185  		}
   186  		prevBytes = pos
   187  	}
   188  	if allUpper {
   189  		// The whole name is upper-case.
   190  		return strings.ToLower(goName)
   191  	}
   192  	if prevBytes == 0 {
   193  		// The name starts with a single upper-case letter. Lower-case just it.
   194  		prevBytes = prefixBytes
   195  	}
   196  	// Lower case the first n-1 upper-case letters.
   197  	return strings.ToLower(goName[:prevBytes]) + goName[prevBytes:]
   198  }
   199  
   200  func (d Tuple) decode(rv reflect.Value) error {
   201  	if d.repeat != nil {
   202  		return &inexactError{"repeated tuple", rv.Type().String()}
   203  	}
   204  	// TODO: We could also do arrays.
   205  	if rv.Kind() != reflect.Slice {
   206  		return fmt.Errorf("cannot decode Tuple into %s", rv.Type())
   207  	}
   208  	if rv.IsNil() || rv.Cap() < len(d.vs) {
   209  		rv.Set(reflect.MakeSlice(rv.Type(), len(d.vs), len(d.vs)))
   210  	} else {
   211  		rv.SetLen(len(d.vs))
   212  	}
   213  	for i, v := range d.vs {
   214  		if err := decodeReflect(v, rv.Index(i)); err != nil {
   215  			return newDecodeError(fmt.Sprintf("%d", i), err)
   216  		}
   217  	}
   218  	return nil
   219  }
   220  
   221  func (d String) decode(rv reflect.Value) error {
   222  	if d.kind != stringExact {
   223  		return &inexactError{"regex", rv.Type().String()}
   224  	}
   225  	switch rv.Kind() {
   226  	default:
   227  		return fmt.Errorf("cannot decode String into %s", rv.Type())
   228  	case reflect.String:
   229  		rv.SetString(d.exact)
   230  	case reflect.Int:
   231  		i, err := strconv.Atoi(d.exact)
   232  		if err != nil {
   233  			return fmt.Errorf("cannot decode String into %s: %s", rv.Type(), err)
   234  		}
   235  		rv.SetInt(int64(i))
   236  	case reflect.Bool:
   237  		b, err := strconv.ParseBool(d.exact)
   238  		if err != nil {
   239  			return fmt.Errorf("cannot decode String into %s: %s", rv.Type(), err)
   240  		}
   241  		rv.SetBool(b)
   242  	}
   243  	return nil
   244  }
   245  

View as plain text