// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package unify import ( "fmt" "iter" "reflect" ) // A Value represents a structured, non-deterministic value consisting of // strings, tuples of Values, and string-keyed maps of Values. A // non-deterministic Value will also contain variables, which are resolved via // an environment as part of a [Closure]. // // For debugging, a Value can also track the source position it was read from in // an input file, and its provenance from other Values. type Value struct { Domain Domain // A Value has either a pos or parents (or neither). pos *Pos parents *[2]*Value } var ( topValue = &Value{Domain: Top{}} bottomValue = &Value{Domain: nil} ) // NewValue returns a new [Value] with the given domain and no position // information. func NewValue(d Domain) *Value { return &Value{Domain: d} } // NewValuePos returns a new [Value] with the given domain at position p. func NewValuePos(d Domain, p Pos) *Value { return &Value{Domain: d, pos: &p} } // newValueFrom returns a new [Value] with the given domain that copies the // position information of p. func newValueFrom(d Domain, p *Value) *Value { return &Value{Domain: d, pos: p.pos, parents: p.parents} } func unified(d Domain, p1, p2 *Value) *Value { return &Value{Domain: d, parents: &[2]*Value{p1, p2}} } func (v *Value) Pos() Pos { if v.pos == nil { return Pos{} } return *v.pos } func (v *Value) PosString() string { var b []byte for root := range v.Provenance() { if len(b) > 0 { b = append(b, ' ') } b, _ = root.pos.AppendText(b) } return string(b) } func (v *Value) WhyNotExact() string { if v.Domain == nil { return "v.Domain is nil" } return v.Domain.WhyNotExact() } func (v *Value) Exact() bool { if v.Domain == nil { return false } return v.Domain.Exact() } // Decode decodes v into a Go value. // // v must be exact, except that it can include Top. into must be a pointer. // [Def]s are decoded into structs. [Tuple]s are decoded into slices. [String]s // are decoded into strings or ints. Any field can itself be a pointer to one of // these types. Top can be decoded into a pointer-typed field and will set the // field to nil. Anything else will allocate a value if necessary. // // Any type may implement [Decoder], in which case its DecodeUnified method will // be called instead of using the default decoding scheme. func (v *Value) Decode(into any) error { rv := reflect.ValueOf(into) if rv.Kind() != reflect.Pointer { return fmt.Errorf("cannot decode into non-pointer %T", into) } return decodeReflect(v, rv.Elem()) } func decodeReflect(v *Value, rv reflect.Value) error { var ptr reflect.Value if rv.Kind() == reflect.Pointer { if rv.IsNil() { // Transparently allocate through pointers, *except* for Top, which // wants to set the pointer to nil. // // TODO: Drop this condition if I switch to an explicit Optional[T] // or move the Top logic into Def. if _, ok := v.Domain.(Top); !ok { // Allocate the value to fill in, but don't actually store it in // the pointer until we successfully decode. ptr = rv rv = reflect.New(rv.Type().Elem()).Elem() } } else { rv = rv.Elem() } } var err error if reflect.PointerTo(rv.Type()).Implements(decoderType) { // Use the custom decoder. err = rv.Addr().Interface().(Decoder).DecodeUnified(v) } else { err = v.Domain.decode(rv) } if err == nil && ptr.IsValid() { ptr.Set(rv.Addr()) } return err } // Decoder can be implemented by types as a custom implementation of [Decode] // for that type. type Decoder interface { DecodeUnified(v *Value) error } var decoderType = reflect.TypeOf((*Decoder)(nil)).Elem() // Provenance iterates over all of the source Values that have contributed to // this Value. func (v *Value) Provenance() iter.Seq[*Value] { return func(yield func(*Value) bool) { var rec func(d *Value) bool rec = func(d *Value) bool { if d.pos != nil { if !yield(d) { return false } } if d.parents != nil { for _, p := range d.parents { if !rec(p) { return false } } } return true } rec(v) } }