- Replace the various subst.XList methods with a generic function.
- Rename comparable function to comparableType to avoid shadowing
predeclared type comparable.
- Rename substFunc/Var to cloneFunc/Var which is more accurate.
Change-Id: I3243f2093e4c43a537766f47e3348402de517090
Reviewed-on: https://go-review.googlesource.com/c/go/+/587775
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
}
// see if we can extract a more specific error
var cause string
- comparable(typ, true, nil, func(format string, args ...interface{}) {
+ comparableType(typ, true, nil, func(format string, args ...interface{}) {
cause = check.sprintf(format, args...)
})
return cause
}
// If T is comparable, V must be comparable.
// If V is strictly comparable, we're done.
- if comparable(V, false /* strict comparability */, nil, nil) {
+ if comparableType(V, false /* strict comparability */, nil, nil) {
return true
}
// For constraint satisfaction, use dynamic (spec) comparability
// so that ordinary, non-type parameter interfaces implement comparable.
- if constraint && comparable(V, true /* spec comparability */, nil, nil) {
+ if constraint && comparableType(V, true /* spec comparability */, nil, nil) {
// V is comparable if we are at Go 1.20 or higher.
if check == nil || check.allowVersion(atPos(pos), go1_20) { // atPos needed so that go/types generate passes
return true
rtyp = t
}
- sig.recv = substVar(origSig.recv, rtyp)
- return substFunc(origm, sig)
+ sig.recv = cloneVar(origSig.recv, rtyp)
+ return cloneFunc(origm, sig)
}
// SetUnderlying sets the underlying type and marks t as complete.
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- return comparable(T, true, nil, nil)
+ return comparableType(T, true, nil, nil)
}
// If dynamic is set, non-type parameter interfaces are always comparable.
// If reportf != nil, it may be used to report why T is not comparable.
-func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
+func comparableType(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
if seen[T] {
return true
}
return true
case *Struct:
for _, f := range t.fields {
- if !comparable(f.typ, dynamic, seen, nil) {
+ if !comparableType(f.typ, dynamic, seen, nil) {
if reportf != nil {
reportf("struct containing %s cannot be compared", f.typ)
}
}
return true
case *Array:
- if !comparable(t.elem, dynamic, seen, nil) {
+ if !comparableType(t.elem, dynamic, seen, nil) {
if reportf != nil {
reportf("%s cannot be compared", t)
}
// For each (existing) type argument determine if it needs
// to be substituted; i.e., if it is or contains a type parameter
// that has a type argument for it.
- targs, updated := subst.typeList(t.TypeArgs().list())
- if updated {
+ if targs := substList(t.TypeArgs().list(), subst.typ); targs != nil {
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
}
}
case *Struct:
- if fields, copied := subst.varList(t.fields); copied {
+ if fields := substList(t.fields, subst.var_); fields != nil {
s := &Struct{fields: fields, tags: t.tags}
s.markComplete()
return s
}
case *Union:
- terms, copied := subst.termlist(t.terms)
- if copied {
+ if terms := substList(t.terms, subst.term); terms != nil {
// term list substitution may introduce duplicate terms (unlikely but possible).
// This is ok; lazy type set computation will determine the actual type set
// in normal form.
}
case *Interface:
- methods, mcopied := subst.funcList(t.methods)
- embeddeds, ecopied := subst.typeList(t.embeddeds)
- if mcopied || ecopied {
+ methods := substList(t.methods, subst.func_)
+ embeddeds := substList(t.embeddeds, subst.typ)
+ if methods != nil || embeddeds != nil {
+ if methods == nil {
+ methods = t.methods
+ }
+ if embeddeds == nil {
+ embeddeds = t.embeddeds
+ }
iface := subst.check.newInterface()
iface.embeddeds = embeddeds
iface.embedPos = t.embedPos
// For each (existing) type argument determine if it needs
// to be substituted; i.e., if it is or contains a type parameter
// that has a type argument for it.
- targs, updated := subst.typeList(t.TypeArgs().list())
- if updated {
+ if targs := substList(t.TypeArgs().list(), subst.typ); targs != nil {
// Create a new instance and populate the context to avoid endless
// recursion. The position used here is irrelevant because validation only
// occurs on t (we don't call validType on named), but we use subst.pos to
func (subst *subster) var_(v *Var) *Var {
if v != nil {
if typ := subst.typ(v.typ); typ != v.typ {
- return substVar(v, typ)
+ return cloneVar(v, typ)
}
}
return v
}
-func substVar(v *Var, typ Type) *Var {
+func cloneVar(v *Var, typ Type) *Var {
copy := *v
copy.typ = typ
copy.origin = v.Origin()
func (subst *subster) tuple(t *Tuple) *Tuple {
if t != nil {
- if vars, copied := subst.varList(t.vars); copied {
+ if vars := substList(t.vars, subst.var_); vars != nil {
return &Tuple{vars: vars}
}
}
return t
}
-func (subst *subster) varList(in []*Var) (out []*Var, copied bool) {
- out = in
- for i, v := range in {
- if w := subst.var_(v); w != v {
- if !copied {
- // first variable that got substituted => allocate new out slice
- // and copy all variables
- new := make([]*Var, len(in))
- copy(new, out)
- out = new
- copied = true
+// substList applies subst to each element of the incoming slice.
+// If at least one element changes, the result is a new slice with
+// all the (possibly updated) elements of the incoming slice;
+// otherwise the result it nil. The incoming slice is unchanged.
+func substList[T comparable](in []T, subst func(T) T) (out []T) {
+ for i, t := range in {
+ if u := subst(t); u != t {
+ if out == nil {
+ // lazily allocate a new slice on first substitution
+ out = make([]T, len(in))
+ copy(out, in)
}
- out[i] = w
+ out[i] = u
}
}
return
func (subst *subster) func_(f *Func) *Func {
if f != nil {
if typ := subst.typ(f.typ); typ != f.typ {
- return substFunc(f, typ)
+ return cloneFunc(f, typ)
}
}
return f
}
-func substFunc(f *Func, typ Type) *Func {
+func cloneFunc(f *Func, typ Type) *Func {
copy := *f
copy.typ = typ
copy.origin = f.Origin()
return ©
}
-func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) {
- out = in
- for i, f := range in {
- if g := subst.func_(f); g != f {
- if !copied {
- // first function that got substituted => allocate new out slice
- // and copy all functions
- new := make([]*Func, len(in))
- copy(new, out)
- out = new
- copied = true
- }
- out[i] = g
- }
- }
- return
-}
-
-func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
- out = in
- for i, t := range in {
- if u := subst.typ(t); u != t {
- if !copied {
- // first function that got substituted => allocate new out slice
- // and copy all functions
- new := make([]Type, len(in))
- copy(new, out)
- out = new
- copied = true
- }
- out[i] = u
- }
- }
- return
-}
-
-func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
- out = in
- for i, t := range in {
- if u := subst.typ(t.typ); u != t.typ {
- if !copied {
- // first function that got substituted => allocate new out slice
- // and copy all functions
- new := make([]*Term, len(in))
- copy(new, out)
- out = new
- copied = true
- }
- out[i] = NewTerm(t.tilde, u)
- }
+func (subst *subster) term(t *Term) *Term {
+ if typ := subst.typ(t.typ); typ != t.typ {
+ return NewTerm(t.tilde, typ)
}
- return
+ return t
}
// replaceRecvType updates any function receivers that have type old to have
copied = true
}
newsig := *sig
- newsig.recv = substVar(sig.recv, new)
- out[i] = substFunc(method, &newsig)
+ newsig.recv = cloneVar(sig.recv, new)
+ out[i] = cloneFunc(method, &newsig)
}
}
return
return s.comparable
}
return s.is(func(t *term) bool {
- return t != nil && comparable(t.typ, false, seen, nil)
+ return t != nil && comparableType(t.typ, false, seen, nil)
})
}
i := 0
for _, t := range terms {
assert(t.typ != nil)
- if comparable(t.typ, false /* strictly comparable */, nil, nil) {
+ if comparableType(t.typ, false /* strictly comparable */, nil, nil) {
terms[i] = t
i++
}
}
// see if we can extract a more specific error
var cause string
- comparable(typ, true, nil, func(format string, args ...interface{}) {
+ comparableType(typ, true, nil, func(format string, args ...interface{}) {
cause = check.sprintf(format, args...)
})
return cause
}
// If T is comparable, V must be comparable.
// If V is strictly comparable, we're done.
- if comparable(V, false /* strict comparability */, nil, nil) {
+ if comparableType(V, false /* strict comparability */, nil, nil) {
return true
}
// For constraint satisfaction, use dynamic (spec) comparability
// so that ordinary, non-type parameter interfaces implement comparable.
- if constraint && comparable(V, true /* spec comparability */, nil, nil) {
+ if constraint && comparableType(V, true /* spec comparability */, nil, nil) {
// V is comparable if we are at Go 1.20 or higher.
if check == nil || check.allowVersion(atPos(pos), go1_20) { // atPos needed so that go/types generate passes
return true
rtyp = t
}
- sig.recv = substVar(origSig.recv, rtyp)
- return substFunc(origm, sig)
+ sig.recv = cloneVar(origSig.recv, rtyp)
+ return cloneFunc(origm, sig)
}
// SetUnderlying sets the underlying type and marks t as complete.
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- return comparable(T, true, nil, nil)
+ return comparableType(T, true, nil, nil)
}
// If dynamic is set, non-type parameter interfaces are always comparable.
// If reportf != nil, it may be used to report why T is not comparable.
-func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
+func comparableType(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
if seen[T] {
return true
}
return true
case *Struct:
for _, f := range t.fields {
- if !comparable(f.typ, dynamic, seen, nil) {
+ if !comparableType(f.typ, dynamic, seen, nil) {
if reportf != nil {
reportf("struct containing %s cannot be compared", f.typ)
}
}
return true
case *Array:
- if !comparable(t.elem, dynamic, seen, nil) {
+ if !comparableType(t.elem, dynamic, seen, nil) {
if reportf != nil {
reportf("%s cannot be compared", t)
}
// For each (existing) type argument determine if it needs
// to be substituted; i.e., if it is or contains a type parameter
// that has a type argument for it.
- targs, updated := subst.typeList(t.TypeArgs().list())
- if updated {
+ if targs := substList(t.TypeArgs().list(), subst.typ); targs != nil {
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
}
}
case *Struct:
- if fields, copied := subst.varList(t.fields); copied {
+ if fields := substList(t.fields, subst.var_); fields != nil {
s := &Struct{fields: fields, tags: t.tags}
s.markComplete()
return s
}
case *Union:
- terms, copied := subst.termlist(t.terms)
- if copied {
+ if terms := substList(t.terms, subst.term); terms != nil {
// term list substitution may introduce duplicate terms (unlikely but possible).
// This is ok; lazy type set computation will determine the actual type set
// in normal form.
}
case *Interface:
- methods, mcopied := subst.funcList(t.methods)
- embeddeds, ecopied := subst.typeList(t.embeddeds)
- if mcopied || ecopied {
+ methods := substList(t.methods, subst.func_)
+ embeddeds := substList(t.embeddeds, subst.typ)
+ if methods != nil || embeddeds != nil {
+ if methods == nil {
+ methods = t.methods
+ }
+ if embeddeds == nil {
+ embeddeds = t.embeddeds
+ }
iface := subst.check.newInterface()
iface.embeddeds = embeddeds
iface.embedPos = t.embedPos
// For each (existing) type argument determine if it needs
// to be substituted; i.e., if it is or contains a type parameter
// that has a type argument for it.
- targs, updated := subst.typeList(t.TypeArgs().list())
- if updated {
+ if targs := substList(t.TypeArgs().list(), subst.typ); targs != nil {
// Create a new instance and populate the context to avoid endless
// recursion. The position used here is irrelevant because validation only
// occurs on t (we don't call validType on named), but we use subst.pos to
func (subst *subster) var_(v *Var) *Var {
if v != nil {
if typ := subst.typ(v.typ); typ != v.typ {
- return substVar(v, typ)
+ return cloneVar(v, typ)
}
}
return v
}
-func substVar(v *Var, typ Type) *Var {
+func cloneVar(v *Var, typ Type) *Var {
copy := *v
copy.typ = typ
copy.origin = v.Origin()
func (subst *subster) tuple(t *Tuple) *Tuple {
if t != nil {
- if vars, copied := subst.varList(t.vars); copied {
+ if vars := substList(t.vars, subst.var_); vars != nil {
return &Tuple{vars: vars}
}
}
return t
}
-func (subst *subster) varList(in []*Var) (out []*Var, copied bool) {
- out = in
- for i, v := range in {
- if w := subst.var_(v); w != v {
- if !copied {
- // first variable that got substituted => allocate new out slice
- // and copy all variables
- new := make([]*Var, len(in))
- copy(new, out)
- out = new
- copied = true
+// substList applies subst to each element of the incoming slice.
+// If at least one element changes, the result is a new slice with
+// all the (possibly updated) elements of the incoming slice;
+// otherwise the result it nil. The incoming slice is unchanged.
+func substList[T comparable](in []T, subst func(T) T) (out []T) {
+ for i, t := range in {
+ if u := subst(t); u != t {
+ if out == nil {
+ // lazily allocate a new slice on first substitution
+ out = make([]T, len(in))
+ copy(out, in)
}
- out[i] = w
+ out[i] = u
}
}
return
func (subst *subster) func_(f *Func) *Func {
if f != nil {
if typ := subst.typ(f.typ); typ != f.typ {
- return substFunc(f, typ)
+ return cloneFunc(f, typ)
}
}
return f
}
-func substFunc(f *Func, typ Type) *Func {
+func cloneFunc(f *Func, typ Type) *Func {
copy := *f
copy.typ = typ
copy.origin = f.Origin()
return ©
}
-func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) {
- out = in
- for i, f := range in {
- if g := subst.func_(f); g != f {
- if !copied {
- // first function that got substituted => allocate new out slice
- // and copy all functions
- new := make([]*Func, len(in))
- copy(new, out)
- out = new
- copied = true
- }
- out[i] = g
- }
- }
- return
-}
-
-func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
- out = in
- for i, t := range in {
- if u := subst.typ(t); u != t {
- if !copied {
- // first function that got substituted => allocate new out slice
- // and copy all functions
- new := make([]Type, len(in))
- copy(new, out)
- out = new
- copied = true
- }
- out[i] = u
- }
- }
- return
-}
-
-func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
- out = in
- for i, t := range in {
- if u := subst.typ(t.typ); u != t.typ {
- if !copied {
- // first function that got substituted => allocate new out slice
- // and copy all functions
- new := make([]*Term, len(in))
- copy(new, out)
- out = new
- copied = true
- }
- out[i] = NewTerm(t.tilde, u)
- }
+func (subst *subster) term(t *Term) *Term {
+ if typ := subst.typ(t.typ); typ != t.typ {
+ return NewTerm(t.tilde, typ)
}
- return
+ return t
}
// replaceRecvType updates any function receivers that have type old to have
copied = true
}
newsig := *sig
- newsig.recv = substVar(sig.recv, new)
- out[i] = substFunc(method, &newsig)
+ newsig.recv = cloneVar(sig.recv, new)
+ out[i] = cloneFunc(method, &newsig)
}
}
return
return s.comparable
}
return s.is(func(t *term) bool {
- return t != nil && comparable(t.typ, false, seen, nil)
+ return t != nil && comparableType(t.typ, false, seen, nil)
})
}
i := 0
for _, t := range terms {
assert(t.typ != nil)
- if comparable(t.typ, false /* strictly comparable */, nil, nil) {
+ if comparableType(t.typ, false /* strictly comparable */, nil, nil) {
terms[i] = t
i++
}