]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: make typeset return an iterator
authorAlan Donovan <adonovan@google.com>
Fri, 18 Jul 2025 18:19:26 +0000 (14:19 -0400)
committerGopher Robot <gobot@golang.org>
Thu, 25 Sep 2025 15:00:44 +0000 (08:00 -0700)
typeset(t) now returns a func equivalent to iter.Seq2[Type, Type]
for the sequence over (type, underlying) pairs in the typeset of t.

underIs was modified to take advantage of the underlying iteration
primitive, all, which computes the desired boolean conjunction
directly.

Change-Id: I3e17d5970fd2908c5dca0754db3e251bf1200af2
Reviewed-on: https://go-review.googlesource.com/c/go/+/688876
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
12 files changed:
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/index.go
src/cmd/compile/internal/types2/signature.go
src/cmd/compile/internal/types2/typeparam.go
src/cmd/compile/internal/types2/typeset.go
src/cmd/compile/internal/types2/under.go
src/go/types/builtins.go
src/go/types/index.go
src/go/types/signature.go
src/go/types/typeparam.go
src/go/types/typeset.go
src/go/types/under.go

index 3de2857ed4828c765218c52a91b334235a51941a..df207a2746d627a746186a7997674151ccf1635d 100644 (file)
@@ -98,17 +98,17 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                        if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
                                y := args[1]
                                hasString := false
-                               typeset(y.typ, func(_, u Type) bool {
+                               for _, u := range typeset(y.typ) {
                                        if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
-                                               return true
-                                       }
-                                       if isString(u) {
+                                               // typeset ⊇ {[]byte}
+                                       } else if isString(u) {
+                                               // typeset ⊇ {string}
                                                hasString = true
-                                               return true
+                                       } else {
+                                               y = nil
+                                               break
                                        }
-                                       y = nil
-                                       return false
-                               })
+                               }
                                if y != nil && hasString {
                                        // setting the signature also signals that we're done
                                        sig = makeSig(x.typ, x.typ, y.typ)
@@ -368,16 +368,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                var special bool
                if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
                        special = true
-                       typeset(y.typ, func(_, u Type) bool {
+                       for _, u := range typeset(y.typ) {
                                if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
-                                       return true
-                               }
-                               if isString(u) {
-                                       return true
+                                       // typeset ⊇ {[]byte}
+                               } else if isString(u) {
+                                       // typeset ⊇ {string}
+                               } else {
+                                       special = false
+                                       break
                                }
-                               special = false
-                               return false
-                       })
+                       }
                }
 
                // general case
@@ -980,29 +980,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 // or a type error if x is not a slice (or a type set of slices).
 func sliceElem(x *operand) (Type, *typeError) {
        var E Type
-       var err *typeError
-       typeset(x.typ, func(_, u Type) bool {
+       for _, u := range typeset(x.typ) {
                s, _ := u.(*Slice)
                if s == nil {
                        if x.isNil() {
                                // Printing x in this case would just print "nil".
                                // Special case this so we can emphasize "untyped".
-                               err = typeErrorf("argument must be a slice; have untyped nil")
+                               return nil, typeErrorf("argument must be a slice; have untyped nil")
                        } else {
-                               err = typeErrorf("argument must be a slice; have %s", x)
+                               return nil, typeErrorf("argument must be a slice; have %s", x)
                        }
-                       return false
                }
                if E == nil {
                        E = s.elem
                } else if !Identical(E, s.elem) {
-                       err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
-                       return false
+                       return nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
                }
-               return true
-       })
-       if err != nil {
-               return nil, err
        }
        return E, nil
 }
index 80e8514168cfff1017bb54ef581ee7b76d439bef..7e16a87332dcdf946764dbe9041a01191708445d 100644 (file)
@@ -216,11 +216,11 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
        // determine common underlying type cu
        var ct, cu Type // type and respective common underlying type
        var hasString bool
-       typeset(x.typ, func(t, u Type) bool {
+       for t, u := range typeset(x.typ) {
                if u == nil {
                        check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
                        cu = nil
-                       return false
+                       break
                }
 
                // Treat strings like byte slices but remember that we saw a string.
@@ -232,18 +232,16 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
                // If this is the first type we're seeing, we're done.
                if cu == nil {
                        ct, cu = t, u
-                       return true
+                       continue
                }
 
                // Otherwise, the current type must have the same underlying type as all previous types.
                if !Identical(cu, u) {
                        check.errorf(x, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", x, ct, t)
                        cu = nil
-                       return false
+                       break
                }
-
-               return true
-       })
+       }
        if hasString {
                // If we saw a string, proceed with string type,
                // but don't go from untyped string to string.
index eaecb77af52db15b3018b5876c070ea1ffb08bd1..ea1cfd88cc60b40c8d5f38ddf79beb55c3b39c7a 100644 (file)
@@ -49,7 +49,7 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
                }
                last := params.At(n - 1).typ
                var S *Slice
-               typeset(last, func(t, _ Type) bool {
+               for t := range typeset(last) {
                        var s *Slice
                        if isString(t) {
                                s = NewSlice(universeByte)
@@ -60,10 +60,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
                                S = s
                        } else if !Identical(S, s) {
                                S = nil
-                               return false
+                               break
                        }
-                       return true
-               })
+               }
                if S == nil {
                        panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
                }
index a04f928908fbae907ad5c26bbe0875c54482a400..c60b5eb41722e4c9f72b8ee08924a16168361c84 100644 (file)
@@ -155,10 +155,10 @@ func (t *TypeParam) is(f func(*term) bool) bool {
        return t.iface().typeSet().is(f)
 }
 
-// typeset is an iterator over the (type/underlying type) pairs of the
+// typeset reports whether f(t, y) is true for all (type/underlying type) pairs of the
 // specific type terms of t's constraint.
-// If there are no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (t *TypeParam) typeset(yield func(t, u Type) bool) {
-       t.iface().typeSet().typeset(yield)
+// If there are no specific terms, typeset returns f(nil, nil).
+// In any case, typeset is guaranteed to call f at least once.
+func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
+       return t.iface().typeSet().all(f)
 }
index 74436952f20fa4f1a122e506ffae9c697b39ae02..ce487e74f7232aeea1eaf76e53c27c6684ae0839 100644 (file)
@@ -104,13 +104,12 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
 // subsetOf reports whether s1 ⊆ s2.
 func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
 
-// typeset is an iterator over the (type/underlying type) pairs in s.
-// If s has no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
+// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
+// If s has no specific terms, all calls f(nil, nil).
+// In any case, all is guaranteed to call f at least once.
+func (s *_TypeSet) all(f func(t, u Type) bool) bool {
        if !s.hasTerms() {
-               yield(nil, nil)
-               return
+               return f(nil, nil)
        }
 
        for _, t := range s.terms {
@@ -123,10 +122,11 @@ func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
                if debug {
                        assert(Identical(u, under(u)))
                }
-               if !yield(t.typ, u) {
-                       break
+               if !f(t.typ, u) {
+                       return false
                }
        }
+       return true
 }
 
 // is calls f with the specific type terms of s and reports whether
index 9e5334b7245edcfdca0cefd6f4e5b9b35975aad8..078ba9ab172453b1ecd0b3a97f50754584c639de 100644 (file)
@@ -4,6 +4,8 @@
 
 package types2
 
+import "iter"
+
 // under returns the true expanded underlying type.
 // If it doesn't exist, the result is Typ[Invalid].
 // under must only be called when a type is known
@@ -18,12 +20,18 @@ func under(t Type) Type {
 // If typ is a type parameter, underIs returns the result of typ.underIs(f).
 // Otherwise, underIs returns the result of f(under(typ)).
 func underIs(typ Type, f func(Type) bool) bool {
-       var ok bool
-       typeset(typ, func(_, u Type) bool {
-               ok = f(u)
-               return ok
+       return all(typ, func(_, u Type) bool {
+               return f(u)
        })
-       return ok
+}
+
+// all reports whether f(t, u) is true for all (type/underlying type)
+// pairs in the typeset of t. See [typeset] for details of sequence.
+func all(t Type, f func(t, u Type) bool) bool {
+       if p, _ := Unalias(t).(*TypeParam); p != nil {
+               return p.typeset(f)
+       }
+       return f(t, under(t))
 }
 
 // typeset is an iterator over the (type/underlying type) pairs of the
@@ -32,12 +40,10 @@ func underIs(typ Type, f func(Type) bool) bool {
 // In that case, if there are no specific terms, typeset calls yield with (nil, nil).
 // If t is not a type parameter, the implied type set consists of just t.
 // In any case, typeset is guaranteed to call yield at least once.
-func typeset(t Type, yield func(t, u Type) bool) {
-       if p, _ := Unalias(t).(*TypeParam); p != nil {
-               p.typeset(yield)
-               return
+func typeset(t Type) iter.Seq2[Type, Type] {
+       return func(yield func(t, u Type) bool) {
+               _ = all(t, yield)
        }
-       yield(t, under(t))
 }
 
 // A typeError describes a type error.
@@ -80,35 +86,28 @@ func (err *typeError) format(check *Checker) string {
 // with the single type t in its type set.
 func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
        var ct, cu Type // type and respective common underlying type
-       var err *typeError
-
-       bad := func(format string, args ...any) bool {
-               err = typeErrorf(format, args...)
-               return false
-       }
-
-       typeset(t, func(t, u Type) bool {
+       for t, u := range typeset(t) {
                if cond != nil {
-                       if err = cond(t, u); err != nil {
-                               return false
+                       if err := cond(t, u); err != nil {
+                               return nil, err
                        }
                }
 
                if u == nil {
-                       return bad("no specific type")
+                       return nil, typeErrorf("no specific type")
                }
 
                // If this is the first type we're seeing, we're done.
                if cu == nil {
                        ct, cu = t, u
-                       return true
+                       continue
                }
 
                // If we've seen a channel before, and we have a channel now, they must be compatible.
                if chu, _ := cu.(*Chan); chu != nil {
                        if ch, _ := u.(*Chan); ch != nil {
                                if !Identical(chu.elem, ch.elem) {
-                                       return bad("channels %s and %s have different element types", ct, t)
+                                       return nil, typeErrorf("channels %s and %s have different element types", ct, t)
                                }
                                // If we have different channel directions, keep the restricted one
                                // and complain if they conflict.
@@ -118,22 +117,16 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
                                case chu.dir == SendRecv:
                                        ct, cu = t, u // switch to restricted channel
                                case ch.dir != SendRecv:
-                                       return bad("channels %s and %s have conflicting directions", ct, t)
+                                       return nil, typeErrorf("channels %s and %s have conflicting directions", ct, t)
                                }
-                               return true
+                               continue
                        }
                }
 
                // Otherwise, the current type must have the same underlying type as all previous types.
                if !Identical(cu, u) {
-                       return bad("%s and %s have different underlying types", ct, t)
+                       return nil, typeErrorf("%s and %s have different underlying types", ct, t)
                }
-
-               return true
-       })
-
-       if err != nil {
-               return nil, err
        }
        return cu, nil
 }
index 1163321ecd756e5f831b2bcdd10acee090277170..9b03a40cbc34409cdca12efbbd3165951ceb8701 100644 (file)
@@ -101,17 +101,17 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                        if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
                                y := args[1]
                                hasString := false
-                               typeset(y.typ, func(_, u Type) bool {
+                               for _, u := range typeset(y.typ) {
                                        if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
-                                               return true
-                                       }
-                                       if isString(u) {
+                                               // typeset ⊇ {[]byte}
+                                       } else if isString(u) {
+                                               // typeset ⊇ {string}
                                                hasString = true
-                                               return true
+                                       } else {
+                                               y = nil
+                                               break
                                        }
-                                       y = nil
-                                       return false
-                               })
+                               }
                                if y != nil && hasString {
                                        // setting the signature also signals that we're done
                                        sig = makeSig(x.typ, x.typ, y.typ)
@@ -371,16 +371,16 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                var special bool
                if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
                        special = true
-                       typeset(y.typ, func(_, u Type) bool {
+                       for _, u := range typeset(y.typ) {
                                if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
-                                       return true
-                               }
-                               if isString(u) {
-                                       return true
+                                       // typeset ⊇ {[]byte}
+                               } else if isString(u) {
+                                       // typeset ⊇ {string}
+                               } else {
+                                       special = false
+                                       break
                                }
-                               special = false
-                               return false
-                       })
+                       }
                }
 
                // general case
@@ -983,29 +983,22 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 // or a type error if x is not a slice (or a type set of slices).
 func sliceElem(x *operand) (Type, *typeError) {
        var E Type
-       var err *typeError
-       typeset(x.typ, func(_, u Type) bool {
+       for _, u := range typeset(x.typ) {
                s, _ := u.(*Slice)
                if s == nil {
                        if x.isNil() {
                                // Printing x in this case would just print "nil".
                                // Special case this so we can emphasize "untyped".
-                               err = typeErrorf("argument must be a slice; have untyped nil")
+                               return nil, typeErrorf("argument must be a slice; have untyped nil")
                        } else {
-                               err = typeErrorf("argument must be a slice; have %s", x)
+                               return nil, typeErrorf("argument must be a slice; have %s", x)
                        }
-                       return false
                }
                if E == nil {
                        E = s.elem
                } else if !Identical(E, s.elem) {
-                       err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
-                       return false
+                       return nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
                }
-               return true
-       })
-       if err != nil {
-               return nil, err
        }
        return E, nil
 }
index 58c8893a8d1aabfd62e8dfe60b9380d43a781756..1d4f36dcf3c57798fb585384c41fa6b910f17eef 100644 (file)
@@ -218,7 +218,8 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
        // determine common underlying type cu
        var ct, cu Type // type and respective common underlying type
        var hasString bool
-       typeset(x.typ, func(t, u Type) bool {
+       // TODO(adonovan): use go1.23 "range typeset()".
+       typeset(x.typ)(func(t, u Type) bool {
                if u == nil {
                        check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
                        cu = nil
index f059ecb183a73adc980943f8398ca502e604fddb..fa41c797b29403c02e4506e16bd2e41c3306e38c 100644 (file)
@@ -62,7 +62,7 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
                }
                last := params.At(n - 1).typ
                var S *Slice
-               typeset(last, func(t, _ Type) bool {
+               for t := range typeset(last) {
                        var s *Slice
                        if isString(t) {
                                s = NewSlice(universeByte)
@@ -73,10 +73,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
                                S = s
                        } else if !Identical(S, s) {
                                S = nil
-                               return false
+                               break
                        }
-                       return true
-               })
+               }
                if S == nil {
                        panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
                }
index cdcd552739f038d21ced5949ba69044514ee74eb..2ffef8f613319a1243663d2f9463c46c02c064ae 100644 (file)
@@ -158,10 +158,10 @@ func (t *TypeParam) is(f func(*term) bool) bool {
        return t.iface().typeSet().is(f)
 }
 
-// typeset is an iterator over the (type/underlying type) pairs of the
+// typeset reports whether f(t, y) is true for all (type/underlying type) pairs of the
 // specific type terms of t's constraint.
-// If there are no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (t *TypeParam) typeset(yield func(t, u Type) bool) {
-       t.iface().typeSet().typeset(yield)
+// If there are no specific terms, typeset returns f(nil, nil).
+// In any case, typeset is guaranteed to call f at least once.
+func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
+       return t.iface().typeSet().all(f)
 }
index dd384e8504f3a18a46c2af91c9792f459a0fe8e6..46ed5ce1802e0bfd9fe58e470d84359244833663 100644 (file)
@@ -107,13 +107,12 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
 // subsetOf reports whether s1 ⊆ s2.
 func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
 
-// typeset is an iterator over the (type/underlying type) pairs in s.
-// If s has no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
+// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
+// If s has no specific terms, all calls f(nil, nil).
+// In any case, all is guaranteed to call f at least once.
+func (s *_TypeSet) all(f func(t, u Type) bool) bool {
        if !s.hasTerms() {
-               yield(nil, nil)
-               return
+               return f(nil, nil)
        }
 
        for _, t := range s.terms {
@@ -126,10 +125,11 @@ func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
                if debug {
                        assert(Identical(u, under(u)))
                }
-               if !yield(t.typ, u) {
-                       break
+               if !f(t.typ, u) {
+                       return false
                }
        }
+       return true
 }
 
 // is calls f with the specific type terms of s and reports whether
index 2c09c491348db76d9a269ab58aa73d4eb14913cf..43bf0ad07cd25fb74314c7ec2d9a835222267bd5 100644 (file)
@@ -7,6 +7,8 @@
 
 package types
 
+import "iter"
+
 // under returns the true expanded underlying type.
 // If it doesn't exist, the result is Typ[Invalid].
 // under must only be called when a type is known
@@ -21,12 +23,18 @@ func under(t Type) Type {
 // If typ is a type parameter, underIs returns the result of typ.underIs(f).
 // Otherwise, underIs returns the result of f(under(typ)).
 func underIs(typ Type, f func(Type) bool) bool {
-       var ok bool
-       typeset(typ, func(_, u Type) bool {
-               ok = f(u)
-               return ok
+       return all(typ, func(_, u Type) bool {
+               return f(u)
        })
-       return ok
+}
+
+// all reports whether f(t, u) is true for all (type/underlying type)
+// pairs in the typeset of t. See [typeset] for details of sequence.
+func all(t Type, f func(t, u Type) bool) bool {
+       if p, _ := Unalias(t).(*TypeParam); p != nil {
+               return p.typeset(f)
+       }
+       return f(t, under(t))
 }
 
 // typeset is an iterator over the (type/underlying type) pairs of the
@@ -35,12 +43,10 @@ func underIs(typ Type, f func(Type) bool) bool {
 // In that case, if there are no specific terms, typeset calls yield with (nil, nil).
 // If t is not a type parameter, the implied type set consists of just t.
 // In any case, typeset is guaranteed to call yield at least once.
-func typeset(t Type, yield func(t, u Type) bool) {
-       if p, _ := Unalias(t).(*TypeParam); p != nil {
-               p.typeset(yield)
-               return
+func typeset(t Type) iter.Seq2[Type, Type] {
+       return func(yield func(t, u Type) bool) {
+               _ = all(t, yield)
        }
-       yield(t, under(t))
 }
 
 // A typeError describes a type error.
@@ -83,35 +89,28 @@ func (err *typeError) format(check *Checker) string {
 // with the single type t in its type set.
 func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
        var ct, cu Type // type and respective common underlying type
-       var err *typeError
-
-       bad := func(format string, args ...any) bool {
-               err = typeErrorf(format, args...)
-               return false
-       }
-
-       typeset(t, func(t, u Type) bool {
+       for t, u := range typeset(t) {
                if cond != nil {
-                       if err = cond(t, u); err != nil {
-                               return false
+                       if err := cond(t, u); err != nil {
+                               return nil, err
                        }
                }
 
                if u == nil {
-                       return bad("no specific type")
+                       return nil, typeErrorf("no specific type")
                }
 
                // If this is the first type we're seeing, we're done.
                if cu == nil {
                        ct, cu = t, u
-                       return true
+                       continue
                }
 
                // If we've seen a channel before, and we have a channel now, they must be compatible.
                if chu, _ := cu.(*Chan); chu != nil {
                        if ch, _ := u.(*Chan); ch != nil {
                                if !Identical(chu.elem, ch.elem) {
-                                       return bad("channels %s and %s have different element types", ct, t)
+                                       return nil, typeErrorf("channels %s and %s have different element types", ct, t)
                                }
                                // If we have different channel directions, keep the restricted one
                                // and complain if they conflict.
@@ -121,22 +120,16 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
                                case chu.dir == SendRecv:
                                        ct, cu = t, u // switch to restricted channel
                                case ch.dir != SendRecv:
-                                       return bad("channels %s and %s have conflicting directions", ct, t)
+                                       return nil, typeErrorf("channels %s and %s have conflicting directions", ct, t)
                                }
-                               return true
+                               continue
                        }
                }
 
                // Otherwise, the current type must have the same underlying type as all previous types.
                if !Identical(cu, u) {
-                       return bad("%s and %s have different underlying types", ct, t)
+                       return nil, typeErrorf("%s and %s have different underlying types", ct, t)
                }
-
-               return true
-       })
-
-       if err != nil {
-               return nil, err
        }
        return cu, nil
 }