]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: extract gc.eqtype as types.Identical
authorMatthew Dempsky <mdempsky@google.com>
Thu, 18 Oct 2018 22:24:50 +0000 (15:24 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 18 Oct 2018 23:44:39 +0000 (23:44 +0000)
For symmetry with go/types.Identical.

Passes toolstash-check.

Change-Id: Id19c3956e44ed8e2d9f203d15824322cc5842d3d
Reviewed-on: https://go-review.googlesource.com/c/143180
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
13 files changed:
src/cmd/compile/internal/gc/closure.go
src/cmd/compile/internal/gc/const.go
src/cmd/compile/internal/gc/dcl.go
src/cmd/compile/internal/gc/esc.go
src/cmd/compile/internal/gc/export.go
src/cmd/compile/internal/gc/order.go
src/cmd/compile/internal/gc/reflect.go
src/cmd/compile/internal/gc/sinit.go
src/cmd/compile/internal/gc/subr.go
src/cmd/compile/internal/gc/swt.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/compile/internal/gc/walk.go
src/cmd/compile/internal/types/identity.go [new file with mode: 0644]

index dcea567a14901eac10a824b7e3340e4d5dc3cfd6..0736c5be4fbed97d98e91499b4b88a1a210e1802 100644 (file)
@@ -397,7 +397,7 @@ func walkclosure(clo *Node, init *Nodes) *Node {
 
        // non-escaping temp to use, if any.
        if x := prealloc[clo]; x != nil {
-               if !eqtype(typ, x.Type) {
+               if !types.Identical(typ, x.Type) {
                        panic("closure type does not match order's assigned type")
                }
                clos.Left.Right = x
@@ -530,7 +530,7 @@ func walkpartialcall(n *Node, init *Nodes) *Node {
 
        // non-escaping temp to use, if any.
        if x := prealloc[n]; x != nil {
-               if !eqtype(typ, x.Type) {
+               if !types.Identical(typ, x.Type) {
                        panic("partial call type does not match order's assigned type")
                }
                clos.Left.Right = x
index 3c542aafae80aaafb2e0b2b08b8569e090375978..a77759832a32a28264eef95b3bf0d41cdf1f044b 100644 (file)
@@ -311,7 +311,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node {
        }
 
        // avoid repeated calculations, errors
-       if eqtype(n.Type, t) {
+       if types.Identical(n.Type, t) {
                return n
        }
 
index 736ea0a018380b7dd5180c0bd66dda9df39c734b..516c33d0bbb4d2254ae7e132f3ede9f1b05961f3 100644 (file)
@@ -929,7 +929,7 @@ func addmethod(msym *types.Sym, t *types.Type, local, nointerface bool) *types.F
                }
                // eqtype only checks that incoming and result parameters match,
                // so explicitly check that the receiver parameters match too.
-               if !eqtype(t, f.Type) || !eqtype(t.Recv().Type, f.Type.Recv().Type) {
+               if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) {
                        yyerror("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
                }
                return f
index 5beb43d5489ac9470e7a3ad249f845da04efeb00..ad43b3caec1bffddc1ffe125d25613a1bb82d314 100644 (file)
@@ -872,7 +872,7 @@ opSwitch:
                        // it is also a dereference, because it is implicitly
                        // dereferenced (see #12588)
                        if n.Type.IsArray() &&
-                               !(n.Right.Type.IsPtr() && eqtype(n.Right.Type.Elem(), n.Type)) {
+                               !(n.Right.Type.IsPtr() && types.Identical(n.Right.Type.Elem(), n.Type)) {
                                e.escassignWhyWhere(n.List.Second(), n.Right, "range", n)
                        } else {
                                e.escassignDereference(n.List.Second(), n.Right, e.stepAssignWhere(n.List.Second(), n.Right, "range-deref", n))
index 6ee660988a7ceb2a81a13aabde6b1418d56a9d3b..85916509cb9f202acfa3618f9457d0a937c600b6 100644 (file)
@@ -131,7 +131,7 @@ func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
 func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op Op, ctxt Class, t *types.Type) *Node {
        n := importsym(ipkg, s, op)
        if n.Op != ONONAME {
-               if n.Op == op && (n.Class() != ctxt || !eqtype(n.Type, t)) {
+               if n.Op == op && (n.Class() != ctxt || !types.Identical(n.Type, t)) {
                        redeclare(lineno, s, fmt.Sprintf("during import %q", ipkg.Path))
                }
                return nil
index e603a39b2a45311b6cc994122e7e3dd42f0db849..3d0fa6ceb2415109e1d66953e8452e9b94adb6d6 100644 (file)
@@ -68,7 +68,7 @@ func (o *Order) newTemp(t *types.Type, clear bool) *Node {
        key := t.LongString()
        a := o.free[key]
        for i, n := range a {
-               if eqtype(t, n.Type) {
+               if types.Identical(t, n.Type) {
                        v = a[i]
                        a[i] = a[len(a)-1]
                        a = a[:len(a)-1]
index 3ef568230181bb0f0ca2993edbd56fd253593340..2dd81e30f57d99abd6983cfabe2fa8e2f5f3a978 100644 (file)
@@ -405,7 +405,7 @@ func methods(t *types.Type) []*Sig {
 
                if !sig.isym.Siggen() {
                        sig.isym.SetSiggen(true)
-                       if !eqtype(this, it) {
+                       if !types.Identical(this, it) {
                                compiling_wrappers = true
                                genwrapper(it, f, sig.isym)
                                compiling_wrappers = false
@@ -414,7 +414,7 @@ func methods(t *types.Type) []*Sig {
 
                if !sig.tsym.Siggen() {
                        sig.tsym.SetSiggen(true)
-                       if !eqtype(this, t) {
+                       if !types.Identical(this, t) {
                                compiling_wrappers = true
                                genwrapper(t, f, sig.tsym)
                                compiling_wrappers = false
index f24da706252ac51daea28f8b4b4eb5dc180da6de..ac8dbf60173f5045f7e4b54a516a4b4327dc5678 100644 (file)
@@ -288,7 +288,7 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
        orig := r
        r = r.Name.Defn.Right
 
-       for r.Op == OCONVNOP && !eqtype(r.Type, l.Type) {
+       for r.Op == OCONVNOP && !types.Identical(r.Type, l.Type) {
                r = r.Left
        }
 
@@ -833,7 +833,7 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
        var a *Node
        if x := prealloc[n]; x != nil {
                // temp allocated during order.go for dddarg
-               if !eqtype(t, x.Type) {
+               if !types.Identical(t, x.Type) {
                        panic("dotdotdot base type does not match order's assigned type")
                }
 
@@ -1154,7 +1154,7 @@ func oaslit(n *Node, init *Nodes) bool {
                // not a special composite literal assignment
                return false
        }
-       if !eqtype(n.Left.Type, n.Right.Type) {
+       if !types.Identical(n.Left.Type, n.Right.Type) {
                // not a special composite literal assignment
                return false
        }
index fcfc7ac2de7c598127056f2f26a79566545ee24d..8e643e6690a5b636c7acb44e124c37474804e949 100644 (file)
@@ -529,119 +529,6 @@ func methtype(t *types.Type) *types.Type {
        return nil
 }
 
-// eqtype reports whether t1 and t2 are identical, following the spec rules.
-//
-// Any cyclic type must go through a named type, and if one is
-// named, it is only identical to the other if they are the same
-// pointer (t1 == t2), so there's no chance of chasing cycles
-// ad infinitum, so no need for a depth counter.
-func eqtype(t1, t2 *types.Type) bool {
-       return eqtype1(t1, t2, true, nil)
-}
-
-// eqtypeIgnoreTags is like eqtype but it ignores struct tags for struct identity.
-func eqtypeIgnoreTags(t1, t2 *types.Type) bool {
-       return eqtype1(t1, t2, false, nil)
-}
-
-type typePair struct {
-       t1 *types.Type
-       t2 *types.Type
-}
-
-func eqtype1(t1, t2 *types.Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
-       if t1 == t2 {
-               return true
-       }
-       if t1 == nil || t2 == nil || t1.Etype != t2.Etype || t1.Broke() || t2.Broke() {
-               return false
-       }
-       if t1.Sym != nil || t2.Sym != nil {
-               // Special case: we keep byte/uint8 and rune/int32
-               // separate for error messages. Treat them as equal.
-               switch t1.Etype {
-               case TUINT8:
-                       return (t1 == types.Types[TUINT8] || t1 == types.Bytetype) && (t2 == types.Types[TUINT8] || t2 == types.Bytetype)
-               case TINT32:
-                       return (t1 == types.Types[TINT32] || t1 == types.Runetype) && (t2 == types.Types[TINT32] || t2 == types.Runetype)
-               default:
-                       return false
-               }
-       }
-
-       if assumedEqual == nil {
-               assumedEqual = make(map[typePair]struct{})
-       } else if _, ok := assumedEqual[typePair{t1, t2}]; ok {
-               return true
-       }
-       assumedEqual[typePair{t1, t2}] = struct{}{}
-
-       switch t1.Etype {
-       case TINTER:
-               if t1.NumFields() != t2.NumFields() {
-                       return false
-               }
-               for i, f1 := range t1.FieldSlice() {
-                       f2 := t2.Field(i)
-                       if f1.Sym != f2.Sym || !eqtype1(f1.Type, f2.Type, cmpTags, assumedEqual) {
-                               return false
-                       }
-               }
-               return true
-
-       case TSTRUCT:
-               if t1.NumFields() != t2.NumFields() {
-                       return false
-               }
-               for i, f1 := range t1.FieldSlice() {
-                       f2 := t2.Field(i)
-                       if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !eqtype1(f1.Type, f2.Type, cmpTags, assumedEqual) {
-                               return false
-                       }
-                       if cmpTags && f1.Note != f2.Note {
-                               return false
-                       }
-               }
-               return true
-
-       case TFUNC:
-               // Check parameters and result parameters for type equality.
-               // We intentionally ignore receiver parameters for type
-               // equality, because they're never relevant.
-               for _, f := range types.ParamsResults {
-                       // Loop over fields in structs, ignoring argument names.
-                       fs1, fs2 := f(t1).FieldSlice(), f(t2).FieldSlice()
-                       if len(fs1) != len(fs2) {
-                               return false
-                       }
-                       for i, f1 := range fs1 {
-                               f2 := fs2[i]
-                               if f1.Isddd() != f2.Isddd() || !eqtype1(f1.Type, f2.Type, cmpTags, assumedEqual) {
-                                       return false
-                               }
-                       }
-               }
-               return true
-
-       case TARRAY:
-               if t1.NumElem() != t2.NumElem() {
-                       return false
-               }
-
-       case TCHAN:
-               if t1.ChanDir() != t2.ChanDir() {
-                       return false
-               }
-
-       case TMAP:
-               if !eqtype1(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
-                       return false
-               }
-       }
-
-       return eqtype1(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
-}
-
 // Are t1 and t2 equal struct types when field names are ignored?
 // For deciding whether the result struct from g can be copied
 // directly when compiling f(g()).
@@ -655,7 +542,7 @@ func eqtypenoname(t1 *types.Type, t2 *types.Type) bool {
        }
        for i, f1 := range t1.FieldSlice() {
                f2 := t2.Field(i)
-               if !eqtype(f1.Type, f2.Type) {
+               if !types.Identical(f1.Type, f2.Type) {
                        return false
                }
        }
@@ -678,7 +565,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
        }
 
        // 1. src type is identical to dst.
-       if eqtype(src, dst) {
+       if types.Identical(src, dst) {
                return OCONVNOP
        }
 
@@ -689,7 +576,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
        // we want to recompute the itab. Recomputing the itab ensures
        // that itabs are unique (thus an interface with a compile-time
        // type I has an itab with interface type I).
-       if eqtype(src.Orig, dst.Orig) {
+       if types.Identical(src.Orig, dst.Orig) {
                if src.IsEmptyInterface() {
                        // Conversion between two empty interfaces
                        // requires no code.
@@ -757,7 +644,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
        // src and dst have identical element types, and
        // either src or dst is not a named type.
        if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
-               if eqtype(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
+               if types.Identical(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
                        return OCONVNOP
                }
        }
@@ -828,14 +715,14 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op {
        }
 
        // 2. Ignoring struct tags, src and dst have identical underlying types.
-       if eqtypeIgnoreTags(src.Orig, dst.Orig) {
+       if types.IdenticalIgnoreTags(src.Orig, dst.Orig) {
                return OCONVNOP
        }
 
        // 3. src and dst are unnamed pointer types and, ignoring struct tags,
        // their base types have identical underlying types.
        if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil {
-               if eqtypeIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
+               if types.IdenticalIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
                        return OCONVNOP
                }
        }
@@ -938,7 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
                }
        }
 
-       if eqtype(n.Type, t) {
+       if types.Identical(n.Type, t) {
                return n
        }
 
@@ -1804,7 +1691,7 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
                                return false
                        }
                        tm := tms[i]
-                       if !eqtype(tm.Type, im.Type) {
+                       if !types.Identical(tm.Type, im.Type) {
                                *m = im
                                *samename = tm
                                *ptr = 0
@@ -1836,7 +1723,7 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
                        return false
                }
                tm := tms[i]
-               if tm.Nointerface() || !eqtype(tm.Type, im.Type) {
+               if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
                        *m = im
                        *samename = tm
                        *ptr = 0
index b668409a889ac8aa3109dbec0c1a32e2ab37b3b6..965c54566085d73afcc628f2f36b94b1e1f2545b 100644 (file)
@@ -611,7 +611,7 @@ Outer:
                        continue
                }
                for _, n := range prev {
-                       if eqtype(n.Left.Type, c.node.Left.Type) {
+                       if types.Identical(n.Left.Type, c.node.Left.Type) {
                                yyerrorl(c.node.Pos, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line())
                                // avoid double-reporting errors
                                continue Outer
index 617215c7024cdfec4a3464f982be88e6148a0cd3..992e59a0109eb20691091bc205c83b4167de4d61 100644 (file)
@@ -632,7 +632,7 @@ func typecheck1(n *Node, top int) *Node {
                        et = TINT
                }
                aop := OXXX
-               if iscmp[n.Op] && t.Etype != TIDEAL && !eqtype(l.Type, r.Type) {
+               if iscmp[n.Op] && t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
                        // comparison is okay as long as one side is
                        // assignable to the other.  convert so they have
                        // the same type.
@@ -687,7 +687,7 @@ func typecheck1(n *Node, top int) *Node {
                        et = t.Etype
                }
 
-               if t.Etype != TIDEAL && !eqtype(l.Type, r.Type) {
+               if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
                        l, r = defaultlit2(l, r, true)
                        if r.Type.IsInterface() == l.Type.IsInterface() || aop == 0 {
                                yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
@@ -1233,7 +1233,7 @@ func typecheck1(n *Node, top int) *Node {
                        // It isn't necessary, so just do a sanity check.
                        tp := t.Recv().Type
 
-                       if l.Left == nil || !eqtype(l.Left.Type, tp) {
+                       if l.Left == nil || !types.Identical(l.Left.Type, tp) {
                                Fatalf("method receiver")
                        }
 
@@ -1452,7 +1452,7 @@ func typecheck1(n *Node, top int) *Node {
                        n.Right = r
                }
 
-               if !eqtype(l.Type, r.Type) {
+               if !types.Identical(l.Type, r.Type) {
                        yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
                        n.Type = nil
                        return n
@@ -1657,7 +1657,7 @@ func typecheck1(n *Node, top int) *Node {
 
                // copy([]byte, string)
                if n.Left.Type.IsSlice() && n.Right.Type.IsString() {
-                       if eqtype(n.Left.Type.Elem(), types.Bytetype) {
+                       if types.Identical(n.Left.Type.Elem(), types.Bytetype) {
                                break
                        }
                        yyerror("arguments to copy have different element types: %L and string", n.Left.Type)
@@ -1677,7 +1677,7 @@ func typecheck1(n *Node, top int) *Node {
                        return n
                }
 
-               if !eqtype(n.Left.Type.Elem(), n.Right.Type.Elem()) {
+               if !types.Identical(n.Left.Type.Elem(), n.Right.Type.Elem()) {
                        yyerror("arguments to copy have different element types: %L and %L", n.Left.Type, n.Right.Type)
                        n.Type = nil
                        return n
@@ -2479,17 +2479,17 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
                tt := n.Left.Type
                dowidth(tt)
                rcvr := f2.Type.Recv().Type
-               if !eqtype(rcvr, tt) {
-                       if rcvr.IsPtr() && eqtype(rcvr.Elem(), tt) {
+               if !types.Identical(rcvr, tt) {
+                       if rcvr.IsPtr() && types.Identical(rcvr.Elem(), tt) {
                                checklvalue(n.Left, "call pointer method on")
                                n.Left = nod(OADDR, n.Left, nil)
                                n.Left.SetImplicit(true)
                                n.Left = typecheck(n.Left, Etype|Erv)
-                       } else if tt.IsPtr() && !rcvr.IsPtr() && eqtype(tt.Elem(), rcvr) {
+                       } else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) {
                                n.Left = nod(OIND, n.Left, nil)
                                n.Left.SetImplicit(true)
                                n.Left = typecheck(n.Left, Etype|Erv)
-                       } else if tt.IsPtr() && tt.Elem().IsPtr() && eqtype(derefall(tt), derefall(rcvr)) {
+                       } else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) {
                                yyerror("calling method %v with receiver %L requires explicit dereference", n.Sym, n.Left)
                                for tt.IsPtr() {
                                        // Stop one level early for method with pointer receiver.
@@ -2831,7 +2831,7 @@ func keydup(n *Node, hash map[uint32][]*Node) {
                if a.Op == OCONVIFACE && orign.Op == OCONVIFACE {
                        a = a.Left
                }
-               if !eqtype(a.Type, n.Type) {
+               if !types.Identical(a.Type, n.Type) {
                        continue
                }
                cmp.Right = a
@@ -2875,7 +2875,7 @@ func pushtype(n *Node, t *types.Type) {
                n.Right.SetImplicit(true) // * is okay
        } else if Debug['s'] != 0 {
                n.Right = typecheck(n.Right, Etype)
-               if n.Right.Type != nil && eqtype(n.Right.Type, t) {
+               if n.Right.Type != nil && types.Identical(n.Right.Type, t) {
                        fmt.Printf("%v: redundant type: %v\n", n.Line(), t)
                }
        }
@@ -3261,7 +3261,7 @@ func checkassignlist(stmt *Node, l Nodes) {
 // lvalue expression is for OSLICE and OAPPEND optimizations, and it
 // is correct in those settings.
 func samesafeexpr(l *Node, r *Node) bool {
-       if l.Op != r.Op || !eqtype(l.Type, r.Type) {
+       if l.Op != r.Op || !types.Identical(l.Type, r.Type) {
                return false
        }
 
@@ -3702,7 +3702,7 @@ func typecheckdef(n *Node) {
                                goto ret
                        }
 
-                       if !e.Type.IsUntyped() && !eqtype(t, e.Type) {
+                       if !e.Type.IsUntyped() && !types.Identical(t, e.Type) {
                                yyerrorl(n.Pos, "cannot use %L as type %v in const initializer", e, t)
                                goto ret
                        }
index 2d84302116135199c4f7a46e4b5fb24478dbe345..cc4b9ec2d3b64f6beff54c1902c2788435bd4a1e 100644 (file)
@@ -1681,7 +1681,7 @@ func fncall(l *Node, rt *types.Type) bool {
        if l.HasCall() || l.Op == OINDEXMAP {
                return true
        }
-       if eqtype(l.Type, rt) {
+       if types.Identical(l.Type, rt) {
                return false
        }
        // There might be a conversion required, which might involve a runtime call.
@@ -2023,7 +2023,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
                r := nod(OCALL, on, nil)
                if params := on.Type.Params().FieldSlice(); len(params) > 0 {
                        t := params[0].Type
-                       if !eqtype(t, n.Type) {
+                       if !types.Identical(t, n.Type) {
                                n = nod(OCONV, n, nil)
                                n.Type = t
                        }
@@ -2102,7 +2102,7 @@ func convas(n *Node, init *Nodes) *Node {
                return n
        }
 
-       if !eqtype(lt, rt) {
+       if !types.Identical(lt, rt) {
                n.Right = assignconv(n.Right, lt, "assignment")
                n.Right = walkexpr(n.Right, init)
        }
@@ -2575,7 +2575,7 @@ func mkcall1(fn *Node, t *types.Type, init *Nodes, args ...*Node) *Node {
 }
 
 func conv(n *Node, t *types.Type) *Node {
-       if eqtype(n.Type, t) {
+       if types.Identical(n.Type, t) {
                return n
        }
        n = nod(OCONV, n, nil)
@@ -2597,7 +2597,7 @@ func convnop(n *Node, t *types.Type) *Node {
 // We cannot use conv, because we allow converting bool to uint8 here,
 // which is forbidden in user code.
 func byteindex(n *Node) *Node {
-       if eqtype(n.Type, types.Types[TUINT8]) {
+       if types.Identical(n.Type, types.Types[TUINT8]) {
                return n
        }
        n = nod(OCONV, n, nil)
@@ -3457,7 +3457,7 @@ func walkcompare(n *Node, init *Nodes) *Node {
 
 func walkcompareInterface(n *Node, init *Nodes) *Node {
        // ifaceeq(i1 any-1, i2 any-2) (ret bool);
-       if !eqtype(n.Left.Type, n.Right.Type) {
+       if !types.Identical(n.Left.Type, n.Right.Type) {
                Fatalf("ifaceeq %v %v %v", n.Op, n.Left.Type, n.Right.Type)
        }
        var fn *Node
diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go
new file mode 100644 (file)
index 0000000..2152485
--- /dev/null
@@ -0,0 +1,119 @@
+// Copyright 2018 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 types
+
+// Identical reports whether t1 and t2 are identical types, following
+// the spec rules. Receiver parameter types are ignored.
+func Identical(t1, t2 *Type) bool {
+       return identical(t1, t2, true, nil)
+}
+
+// IdenticalIgnoreTags is like Identical, but it ignores struct tags
+// for struct identity.
+func IdenticalIgnoreTags(t1, t2 *Type) bool {
+       return identical(t1, t2, false, nil)
+}
+
+type typePair struct {
+       t1 *Type
+       t2 *Type
+}
+
+func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
+       if t1 == t2 {
+               return true
+       }
+       if t1 == nil || t2 == nil || t1.Etype != t2.Etype || t1.Broke() || t2.Broke() {
+               return false
+       }
+       if t1.Sym != nil || t2.Sym != nil {
+               // Special case: we keep byte/uint8 and rune/int32
+               // separate for error messages. Treat them as equal.
+               switch t1.Etype {
+               case TUINT8:
+                       return (t1 == Types[TUINT8] || t1 == Bytetype) && (t2 == Types[TUINT8] || t2 == Bytetype)
+               case TINT32:
+                       return (t1 == Types[TINT32] || t1 == Runetype) && (t2 == Types[TINT32] || t2 == Runetype)
+               default:
+                       return false
+               }
+       }
+
+       // Any cyclic type must go through a named type, and if one is
+       // named, it is only identical to the other if they are the
+       // same pointer (t1 == t2), so there's no chance of chasing
+       // cycles ad infinitum, so no need for a depth counter.
+       if assumedEqual == nil {
+               assumedEqual = make(map[typePair]struct{})
+       } else if _, ok := assumedEqual[typePair{t1, t2}]; ok {
+               return true
+       }
+       assumedEqual[typePair{t1, t2}] = struct{}{}
+
+       switch t1.Etype {
+       case TINTER:
+               if t1.NumFields() != t2.NumFields() {
+                       return false
+               }
+               for i, f1 := range t1.FieldSlice() {
+                       f2 := t2.Field(i)
+                       if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+                               return false
+                       }
+               }
+               return true
+
+       case TSTRUCT:
+               if t1.NumFields() != t2.NumFields() {
+                       return false
+               }
+               for i, f1 := range t1.FieldSlice() {
+                       f2 := t2.Field(i)
+                       if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+                               return false
+                       }
+                       if cmpTags && f1.Note != f2.Note {
+                               return false
+                       }
+               }
+               return true
+
+       case TFUNC:
+               // Check parameters and result parameters for type equality.
+               // We intentionally ignore receiver parameters for type
+               // equality, because they're never relevant.
+               for _, f := range ParamsResults {
+                       // Loop over fields in structs, ignoring argument names.
+                       fs1, fs2 := f(t1).FieldSlice(), f(t2).FieldSlice()
+                       if len(fs1) != len(fs2) {
+                               return false
+                       }
+                       for i, f1 := range fs1 {
+                               f2 := fs2[i]
+                               if f1.Isddd() != f2.Isddd() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+                                       return false
+                               }
+                       }
+               }
+               return true
+
+       case TARRAY:
+               if t1.NumElem() != t2.NumElem() {
+                       return false
+               }
+
+       case TCHAN:
+               if t1.ChanDir() != t2.ChanDir() {
+                       return false
+               }
+
+       case TMAP:
+               if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
+                       return false
+               }
+       }
+
+       return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
+}