]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: cleanup algtype code
authorMatthew Dempsky <mdempsky@google.com>
Fri, 1 Apr 2016 18:22:03 +0000 (11:22 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Fri, 1 Apr 2016 19:23:53 +0000 (19:23 +0000)
Add AlgKind enum type to represent AFOO values.

Add IsComparable, IsRegularMemory, IncomparableField helper methods to
codify common higher-level idioms.

Passes toolstash -cmp.

Change-Id: I54c544953997a8ccc72396b3058897edcbbea392
Reviewed-on: https://go-review.googlesource.com/21420
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/cmd/compile/internal/gc/alg.go
src/cmd/compile/internal/gc/reflect.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

index de26237b329f8c6971e5e74d6f356c8f7e12defd..43876d8befd1815007cec0aa2636c6cc25aa5919 100644 (file)
@@ -6,9 +6,13 @@ package gc
 
 import "fmt"
 
+// AlgKind describes the kind of algorithms used for comparing and
+// hashing a Type.
+type AlgKind int
+
 const (
        // These values are known by runtime.
-       ANOEQ = iota
+       ANOEQ AlgKind = iota
        AMEM0
        AMEM8
        AMEM16
@@ -22,11 +26,40 @@ const (
        AFLOAT64
        ACPLX64
        ACPLX128
-       AMEM = 100
+
+       // Type can be compared/hashed as regular memory.
+       AMEM AlgKind = 100
+
+       // Type needs special comparison/hashing functions.
+       ASPECIAL AlgKind = -1
 )
 
-func algtype(t *Type) int {
-       a := algtype1(t, nil)
+// IsComparable reports whether t is a comparable type.
+func (t *Type) IsComparable() bool {
+       a, _ := algtype1(t)
+       return a != ANOEQ
+}
+
+// IsRegularMemory reports whether t can be compared/hashed as regular memory.
+func (t *Type) IsRegularMemory() bool {
+       a, _ := algtype1(t)
+       return a == AMEM
+}
+
+// IncomparableField returns an incomparable Field of struct Type t, if any.
+func (t *Type) IncomparableField() *Field {
+       for _, f := range t.FieldSlice() {
+               if !f.Type.IsComparable() {
+                       return f
+               }
+       }
+       return nil
+}
+
+// algtype is like algtype1, except it returns the fixed-width AMEMxx variants
+// instead of the general AMEM kind when possible.
+func algtype(t *Type) AlgKind {
+       a, _ := algtype1(t)
        if a == AMEM {
                switch t.Width {
                case 0:
@@ -47,115 +80,105 @@ func algtype(t *Type) int {
        return a
 }
 
-func algtype1(t *Type, bad **Type) int {
-       if bad != nil {
-               *bad = nil
-       }
+// algtype1 returns the AlgKind used for comparing and hashing Type t.
+// If it returns ANOEQ, it also returns the component type of t that
+// makes it incomparable.
+func algtype1(t *Type) (AlgKind, *Type) {
        if t.Broke {
-               return AMEM
+               return AMEM, nil
        }
        if t.Noalg {
-               return ANOEQ
+               return ANOEQ, t
        }
 
        switch t.Etype {
        case TANY, TFORW:
                // will be defined later.
-               *bad = t
-               return -1
+               return ANOEQ, t
 
        case TINT8, TUINT8, TINT16, TUINT16,
                TINT32, TUINT32, TINT64, TUINT64,
                TINT, TUINT, TUINTPTR,
                TBOOL, TPTR32, TPTR64,
                TCHAN, TUNSAFEPTR:
-               return AMEM
+               return AMEM, nil
 
        case TFUNC, TMAP:
-               if bad != nil {
-                       *bad = t
-               }
-               return ANOEQ
+               return ANOEQ, t
 
        case TFLOAT32:
-               return AFLOAT32
+               return AFLOAT32, nil
 
        case TFLOAT64:
-               return AFLOAT64
+               return AFLOAT64, nil
 
        case TCOMPLEX64:
-               return ACPLX64
+               return ACPLX64, nil
 
        case TCOMPLEX128:
-               return ACPLX128
+               return ACPLX128, nil
 
        case TSTRING:
-               return ASTRING
+               return ASTRING, nil
 
        case TINTER:
                if isnilinter(t) {
-                       return ANILINTER
+                       return ANILINTER, nil
                }
-               return AINTER
+               return AINTER, nil
 
        case TARRAY:
                if t.IsSlice() {
-                       if bad != nil {
-                               *bad = t
-                       }
-                       return ANOEQ
+                       return ANOEQ, t
                }
 
-               a := algtype1(t.Elem(), bad)
+               a, bad := algtype1(t.Elem())
                switch a {
                case AMEM:
-                       return AMEM
+                       return AMEM, nil
                case ANOEQ:
-                       if bad != nil {
-                               *bad = t
-                       }
-                       return ANOEQ
+                       return ANOEQ, bad
                }
 
                switch t.Bound {
                case 0:
                        // We checked above that the element type is comparable.
-                       return AMEM
+                       return AMEM, nil
                case 1:
                        // Single-element array is same as its lone element.
-                       return a
+                       return a, nil
                }
 
-               return -1 // needs special compare
+               return ASPECIAL, nil
 
        case TSTRUCT:
                fields := t.FieldSlice()
 
                // One-field struct is same as that one field alone.
                if len(fields) == 1 && !isblanksym(fields[0].Sym) {
-                       return algtype1(fields[0].Type, bad)
+                       return algtype1(fields[0].Type)
                }
 
                ret := AMEM
                for i, f := range fields {
                        // All fields must be comparable.
-                       a := algtype1(f.Type, bad)
+                       a, bad := algtype1(f.Type)
                        if a == ANOEQ {
-                               return ANOEQ
+                               return ANOEQ, bad
                        }
 
                        // Blank fields, padded fields, fields with non-memory
                        // equality need special compare.
                        if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, i) {
-                               ret = -1
+                               ret = ASPECIAL
                        }
                }
 
-               return ret
+               return ret, nil
        }
 
        Fatalf("algtype1: unexpected type %v", t)
-       return 0
+       return 0, nil
 }
 
 // Generate a helper function to compute the hash of a value of type t.
@@ -239,7 +262,7 @@ func genhash(sym *Sym, t *Type) {
                        }
 
                        // Hash non-memory fields with appropriate hash function.
-                       if algtype1(f.Type, nil) != AMEM {
+                       if !f.Type.IsRegularMemory() {
                                hashel := hashfor(f.Type)
                                call := Nod(OCALL, hashel, nil)
                                nx := NodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
@@ -304,7 +327,7 @@ func genhash(sym *Sym, t *Type) {
 func hashfor(t *Type) *Node {
        var sym *Sym
 
-       switch algtype1(t, nil) {
+       switch a, _ := algtype1(t); a {
        case AMEM:
                Fatalf("hashfor with AMEM type")
        case AINTER:
@@ -435,7 +458,7 @@ func geneq(sym *Sym, t *Type) {
                        }
 
                        // Compare non-memory fields with field equality.
-                       if algtype1(f.Type, nil) != AMEM {
+                       if !f.Type.IsRegularMemory() {
                                and(eqfield(np, nq, f.Sym))
                                i++
                                continue
@@ -560,7 +583,7 @@ func memrun(t *Type, start int) (size int64, next int) {
                        break
                }
                // Also, stop before a blank or non-memory field.
-               if f := t.Field(next); isblanksym(f.Sym) || algtype1(f.Type, nil) != AMEM {
+               if f := t.Field(next); isblanksym(f.Sym) || !f.Type.IsRegularMemory() {
                        break
                }
        }
index a2fdf0449ba2fb913d5f6962bbfaeab45e2e125c..a5c85eb98e266bf5c32e435c5ad258faa0acc46c 100644 (file)
@@ -786,7 +786,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
        dowidth(t)
        alg := algtype(t)
        var algsym *Sym
-       if alg < 0 || alg == AMEM {
+       if alg == ASPECIAL || alg == AMEM {
                algsym = dalgsym(t)
        }
 
@@ -854,7 +854,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
        }
        ot = duint8(s, ot, uint8(i)) // kind
        if algsym == nil {
-               ot = dsymptr(s, ot, dcommontype_algarray, alg*sizeofAlg)
+               ot = dsymptr(s, ot, dcommontype_algarray, int(alg)*sizeofAlg)
        } else {
                ot = dsymptr(s, ot, algsym, 0)
        }
index c40cda0aeea73083a8298963cdb12ade5cbbd2a5..b4acb5b1afc5dd712d8dfa208c6a99f059f31542 100644 (file)
@@ -374,24 +374,15 @@ func saveorignode(n *Node) {
 
 // checkMapKeyType checks that Type key is valid for use as a map key.
 func checkMapKeyType(key *Type) {
-       var bad *Type
-       atype := algtype1(key, &bad)
-       var mtype EType
-       if bad == nil {
-               mtype = key.Etype
-       } else {
-               mtype = bad.Etype
+       alg, bad := algtype1(key)
+       if alg != ANOEQ {
+               return
        }
-       switch mtype {
+       switch bad.Etype {
        default:
-               if atype == ANOEQ {
-                       Yyerror("invalid map key type %v", key)
-               }
-
+               Yyerror("invalid map key type %v", key)
        case TANY:
-               // will be resolved later.
-               break
-
+               // Will be resolved later.
        case TFORW:
                // map[key] used during definition of key.
                // postpone check until key is fully defined.
index ae8af765175b7b2f1b02f9d23529ce76f8458618..996bd69113c1f6dc714ca7efd291f451210da451 100644 (file)
@@ -83,16 +83,17 @@ func typecheckswitch(n *Node) {
                        t = Types[TBOOL]
                }
                if t != nil {
-                       var badtype *Type
                        switch {
                        case !okforeq[t.Etype]:
                                Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
-                       case t.Etype == TARRAY && !t.IsArray():
+                       case t.IsSlice():
                                nilonly = "slice"
-                       case t.Etype == TARRAY && t.IsArray() && algtype1(t, nil) == ANOEQ:
+                       case t.IsArray() && !t.IsComparable():
                                Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
-                       case t.IsStruct() && algtype1(t, &badtype) == ANOEQ:
-                               Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, FmtLong), badtype)
+                       case t.IsStruct():
+                               if f := t.IncomparableField(); f != nil {
+                                       Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, FmtLong), f.Type)
+                               }
                        case t.Etype == TFUNC:
                                nilonly = "func"
                        case t.IsMap():
@@ -139,7 +140,7 @@ func typecheckswitch(n *Node) {
                                                }
                                        case nilonly != "" && !isnil(n1):
                                                Yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
-                                       case t.IsInterface() && !n1.Type.IsInterface() && algtype1(n1.Type, nil) == ANOEQ:
+                                       case t.IsInterface() && !n1.Type.IsInterface() && !n1.Type.IsComparable():
                                                Yyerror("invalid case %v in switch (incomparable type)", Nconv(n1, FmtLong))
                                        }
 
index f0b0f080ff45e7f2dec87b52390dc08e4b85d158..68e29b620a246f93fef67936d61921a5ee4881fe 100644 (file)
@@ -596,7 +596,7 @@ OpSwitch:
                        if r.Type.Etype != TBLANK {
                                aop = assignop(l.Type, r.Type, nil)
                                if aop != 0 {
-                                       if r.Type.IsInterface() && !l.Type.IsInterface() && algtype1(l.Type, nil) == ANOEQ {
+                                       if r.Type.IsInterface() && !l.Type.IsInterface() && !l.Type.IsComparable() {
                                                Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(l.Type))
                                                n.Type = nil
                                                return n
@@ -618,7 +618,7 @@ OpSwitch:
                        if l.Type.Etype != TBLANK {
                                aop = assignop(r.Type, l.Type, nil)
                                if aop != 0 {
-                                       if l.Type.IsInterface() && !r.Type.IsInterface() && algtype1(r.Type, nil) == ANOEQ {
+                                       if l.Type.IsInterface() && !r.Type.IsInterface() && !r.Type.IsComparable() {
                                                Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(r.Type))
                                                n.Type = nil
                                                return n
@@ -657,7 +657,7 @@ OpSwitch:
 
                // okfor allows any array == array, map == map, func == func.
                // restrict to slice/map/func == nil and nil == slice/map/func.
-               if l.Type.IsArray() && algtype1(l.Type, nil) == ANOEQ {
+               if l.Type.IsArray() && !l.Type.IsComparable() {
                        Yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type)
                        n.Type = nil
                        return n
@@ -681,11 +681,12 @@ OpSwitch:
                        return n
                }
 
-               var badtype *Type
-               if l.Type.IsStruct() && algtype1(l.Type, &badtype) == ANOEQ {
-                       Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, badtype)
-                       n.Type = nil
-                       return n
+               if l.Type.IsStruct() {
+                       if f := l.Type.IncomparableField(); f != nil {
+                               Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type)
+                               n.Type = nil
+                               return n
+                       }
                }
 
                t = l.Type
index a3e8a044d009a68ecc2703b595775cba707581b8..931017148b1f51bd242c0c141c77ea4086c61e0e 100644 (file)
@@ -3020,30 +3020,27 @@ func eqfor(t *Type, needsize *int) *Node {
        // a struct/array containing a non-memory field/element.
        // Small memory is handled inline, and single non-memory
        // is handled during type check (OCMPSTR etc).
-       a := algtype1(t, nil)
-
-       if a != AMEM && a != -1 {
-               Fatalf("eqfor %v", t)
-       }
-
-       if a == AMEM {
+       switch a, _ := algtype1(t); a {
+       case AMEM:
                n := syslook("memequal")
                n = substArgTypes(n, t, t)
                *needsize = 1
                return n
+       case ASPECIAL:
+               sym := typesymprefix(".eq", t)
+               n := newname(sym)
+               n.Class = PFUNC
+               ntype := Nod(OTFUNC, nil, nil)
+               ntype.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
+               ntype.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
+               ntype.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TBOOL])))
+               ntype = typecheck(ntype, Etype)
+               n.Type = ntype.Type
+               *needsize = 0
+               return n
        }
-
-       sym := typesymprefix(".eq", t)
-       n := newname(sym)
-       n.Class = PFUNC
-       ntype := Nod(OTFUNC, nil, nil)
-       ntype.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
-       ntype.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
-       ntype.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TBOOL])))
-       ntype = typecheck(ntype, Etype)
-       n.Type = ntype.Type
-       *needsize = 0
-       return n
+       Fatalf("eqfor %v", t)
+       return nil
 }
 
 // The result of walkcompare MUST be assigned back to n, e.g.