Commit 077902d1 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: cleanup algtype code

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: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 58394fd7
......@@ -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
}
}
......
......@@ -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)
}
......
......@@ -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.
......
......@@ -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))
}
......
......@@ -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
......
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment