mirror of
https://github.com/golang/go
synced 2024-11-19 06:34:42 -07:00
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: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
58394fd7d5
commit
077902d1a6
@ -6,9 +6,13 @@ package gc
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
|
// AlgKind describes the kind of algorithms used for comparing and
|
||||||
|
// hashing a Type.
|
||||||
|
type AlgKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// These values are known by runtime.
|
// These values are known by runtime.
|
||||||
ANOEQ = iota
|
ANOEQ AlgKind = iota
|
||||||
AMEM0
|
AMEM0
|
||||||
AMEM8
|
AMEM8
|
||||||
AMEM16
|
AMEM16
|
||||||
@ -22,11 +26,40 @@ const (
|
|||||||
AFLOAT64
|
AFLOAT64
|
||||||
ACPLX64
|
ACPLX64
|
||||||
ACPLX128
|
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 {
|
// IsComparable reports whether t is a comparable type.
|
||||||
a := algtype1(t, nil)
|
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 {
|
if a == AMEM {
|
||||||
switch t.Width {
|
switch t.Width {
|
||||||
case 0:
|
case 0:
|
||||||
@ -47,115 +80,105 @@ func algtype(t *Type) int {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func algtype1(t *Type, bad **Type) int {
|
// algtype1 returns the AlgKind used for comparing and hashing Type t.
|
||||||
if bad != nil {
|
// If it returns ANOEQ, it also returns the component type of t that
|
||||||
*bad = nil
|
// makes it incomparable.
|
||||||
}
|
func algtype1(t *Type) (AlgKind, *Type) {
|
||||||
if t.Broke {
|
if t.Broke {
|
||||||
return AMEM
|
return AMEM, nil
|
||||||
}
|
}
|
||||||
if t.Noalg {
|
if t.Noalg {
|
||||||
return ANOEQ
|
return ANOEQ, t
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t.Etype {
|
switch t.Etype {
|
||||||
case TANY, TFORW:
|
case TANY, TFORW:
|
||||||
// will be defined later.
|
// will be defined later.
|
||||||
*bad = t
|
return ANOEQ, t
|
||||||
return -1
|
|
||||||
|
|
||||||
case TINT8, TUINT8, TINT16, TUINT16,
|
case TINT8, TUINT8, TINT16, TUINT16,
|
||||||
TINT32, TUINT32, TINT64, TUINT64,
|
TINT32, TUINT32, TINT64, TUINT64,
|
||||||
TINT, TUINT, TUINTPTR,
|
TINT, TUINT, TUINTPTR,
|
||||||
TBOOL, TPTR32, TPTR64,
|
TBOOL, TPTR32, TPTR64,
|
||||||
TCHAN, TUNSAFEPTR:
|
TCHAN, TUNSAFEPTR:
|
||||||
return AMEM
|
return AMEM, nil
|
||||||
|
|
||||||
case TFUNC, TMAP:
|
case TFUNC, TMAP:
|
||||||
if bad != nil {
|
return ANOEQ, t
|
||||||
*bad = t
|
|
||||||
}
|
|
||||||
return ANOEQ
|
|
||||||
|
|
||||||
case TFLOAT32:
|
case TFLOAT32:
|
||||||
return AFLOAT32
|
return AFLOAT32, nil
|
||||||
|
|
||||||
case TFLOAT64:
|
case TFLOAT64:
|
||||||
return AFLOAT64
|
return AFLOAT64, nil
|
||||||
|
|
||||||
case TCOMPLEX64:
|
case TCOMPLEX64:
|
||||||
return ACPLX64
|
return ACPLX64, nil
|
||||||
|
|
||||||
case TCOMPLEX128:
|
case TCOMPLEX128:
|
||||||
return ACPLX128
|
return ACPLX128, nil
|
||||||
|
|
||||||
case TSTRING:
|
case TSTRING:
|
||||||
return ASTRING
|
return ASTRING, nil
|
||||||
|
|
||||||
case TINTER:
|
case TINTER:
|
||||||
if isnilinter(t) {
|
if isnilinter(t) {
|
||||||
return ANILINTER
|
return ANILINTER, nil
|
||||||
}
|
}
|
||||||
return AINTER
|
return AINTER, nil
|
||||||
|
|
||||||
case TARRAY:
|
case TARRAY:
|
||||||
if t.IsSlice() {
|
if t.IsSlice() {
|
||||||
if bad != nil {
|
return ANOEQ, t
|
||||||
*bad = t
|
|
||||||
}
|
|
||||||
return ANOEQ
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a := algtype1(t.Elem(), bad)
|
a, bad := algtype1(t.Elem())
|
||||||
switch a {
|
switch a {
|
||||||
case AMEM:
|
case AMEM:
|
||||||
return AMEM
|
return AMEM, nil
|
||||||
case ANOEQ:
|
case ANOEQ:
|
||||||
if bad != nil {
|
return ANOEQ, bad
|
||||||
*bad = t
|
|
||||||
}
|
|
||||||
return ANOEQ
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t.Bound {
|
switch t.Bound {
|
||||||
case 0:
|
case 0:
|
||||||
// We checked above that the element type is comparable.
|
// We checked above that the element type is comparable.
|
||||||
return AMEM
|
return AMEM, nil
|
||||||
case 1:
|
case 1:
|
||||||
// Single-element array is same as its lone element.
|
// Single-element array is same as its lone element.
|
||||||
return a
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1 // needs special compare
|
return ASPECIAL, nil
|
||||||
|
|
||||||
case TSTRUCT:
|
case TSTRUCT:
|
||||||
fields := t.FieldSlice()
|
fields := t.FieldSlice()
|
||||||
|
|
||||||
// One-field struct is same as that one field alone.
|
// One-field struct is same as that one field alone.
|
||||||
if len(fields) == 1 && !isblanksym(fields[0].Sym) {
|
if len(fields) == 1 && !isblanksym(fields[0].Sym) {
|
||||||
return algtype1(fields[0].Type, bad)
|
return algtype1(fields[0].Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := AMEM
|
ret := AMEM
|
||||||
for i, f := range fields {
|
for i, f := range fields {
|
||||||
// All fields must be comparable.
|
// All fields must be comparable.
|
||||||
a := algtype1(f.Type, bad)
|
a, bad := algtype1(f.Type)
|
||||||
if a == ANOEQ {
|
if a == ANOEQ {
|
||||||
return ANOEQ
|
return ANOEQ, bad
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blank fields, padded fields, fields with non-memory
|
// Blank fields, padded fields, fields with non-memory
|
||||||
// equality need special compare.
|
// equality need special compare.
|
||||||
if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, i) {
|
if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, i) {
|
||||||
ret = -1
|
ret = ASPECIAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
Fatalf("algtype1: unexpected type %v", t)
|
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.
|
// 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.
|
// Hash non-memory fields with appropriate hash function.
|
||||||
if algtype1(f.Type, nil) != AMEM {
|
if !f.Type.IsRegularMemory() {
|
||||||
hashel := hashfor(f.Type)
|
hashel := hashfor(f.Type)
|
||||||
call := Nod(OCALL, hashel, nil)
|
call := Nod(OCALL, hashel, nil)
|
||||||
nx := NodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
|
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 {
|
func hashfor(t *Type) *Node {
|
||||||
var sym *Sym
|
var sym *Sym
|
||||||
|
|
||||||
switch algtype1(t, nil) {
|
switch a, _ := algtype1(t); a {
|
||||||
case AMEM:
|
case AMEM:
|
||||||
Fatalf("hashfor with AMEM type")
|
Fatalf("hashfor with AMEM type")
|
||||||
case AINTER:
|
case AINTER:
|
||||||
@ -435,7 +458,7 @@ func geneq(sym *Sym, t *Type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare non-memory fields with field equality.
|
// Compare non-memory fields with field equality.
|
||||||
if algtype1(f.Type, nil) != AMEM {
|
if !f.Type.IsRegularMemory() {
|
||||||
and(eqfield(np, nq, f.Sym))
|
and(eqfield(np, nq, f.Sym))
|
||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
@ -560,7 +583,7 @@ func memrun(t *Type, start int) (size int64, next int) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Also, stop before a blank or non-memory field.
|
// 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
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,7 +786,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
|
|||||||
dowidth(t)
|
dowidth(t)
|
||||||
alg := algtype(t)
|
alg := algtype(t)
|
||||||
var algsym *Sym
|
var algsym *Sym
|
||||||
if alg < 0 || alg == AMEM {
|
if alg == ASPECIAL || alg == AMEM {
|
||||||
algsym = dalgsym(t)
|
algsym = dalgsym(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,7 +854,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
|
|||||||
}
|
}
|
||||||
ot = duint8(s, ot, uint8(i)) // kind
|
ot = duint8(s, ot, uint8(i)) // kind
|
||||||
if algsym == nil {
|
if algsym == nil {
|
||||||
ot = dsymptr(s, ot, dcommontype_algarray, alg*sizeofAlg)
|
ot = dsymptr(s, ot, dcommontype_algarray, int(alg)*sizeofAlg)
|
||||||
} else {
|
} else {
|
||||||
ot = dsymptr(s, ot, algsym, 0)
|
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.
|
// checkMapKeyType checks that Type key is valid for use as a map key.
|
||||||
func checkMapKeyType(key *Type) {
|
func checkMapKeyType(key *Type) {
|
||||||
var bad *Type
|
alg, bad := algtype1(key)
|
||||||
atype := algtype1(key, &bad)
|
if alg != ANOEQ {
|
||||||
var mtype EType
|
return
|
||||||
if bad == nil {
|
|
||||||
mtype = key.Etype
|
|
||||||
} else {
|
|
||||||
mtype = bad.Etype
|
|
||||||
}
|
}
|
||||||
switch mtype {
|
switch bad.Etype {
|
||||||
default:
|
default:
|
||||||
if atype == ANOEQ {
|
Yyerror("invalid map key type %v", key)
|
||||||
Yyerror("invalid map key type %v", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
case TANY:
|
case TANY:
|
||||||
// will be resolved later.
|
// Will be resolved later.
|
||||||
break
|
|
||||||
|
|
||||||
case TFORW:
|
case TFORW:
|
||||||
// map[key] used during definition of key.
|
// map[key] used during definition of key.
|
||||||
// postpone check until key is fully defined.
|
// postpone check until key is fully defined.
|
||||||
|
@ -83,16 +83,17 @@ func typecheckswitch(n *Node) {
|
|||||||
t = Types[TBOOL]
|
t = Types[TBOOL]
|
||||||
}
|
}
|
||||||
if t != nil {
|
if t != nil {
|
||||||
var badtype *Type
|
|
||||||
switch {
|
switch {
|
||||||
case !okforeq[t.Etype]:
|
case !okforeq[t.Etype]:
|
||||||
Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
|
Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
|
||||||
case t.Etype == TARRAY && !t.IsArray():
|
case t.IsSlice():
|
||||||
nilonly = "slice"
|
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))
|
Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
|
||||||
case t.IsStruct() && algtype1(t, &badtype) == ANOEQ:
|
case t.IsStruct():
|
||||||
Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, FmtLong), badtype)
|
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:
|
case t.Etype == TFUNC:
|
||||||
nilonly = "func"
|
nilonly = "func"
|
||||||
case t.IsMap():
|
case t.IsMap():
|
||||||
@ -139,7 +140,7 @@ func typecheckswitch(n *Node) {
|
|||||||
}
|
}
|
||||||
case nilonly != "" && !isnil(n1):
|
case nilonly != "" && !isnil(n1):
|
||||||
Yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
|
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))
|
Yyerror("invalid case %v in switch (incomparable type)", Nconv(n1, FmtLong))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,7 +596,7 @@ OpSwitch:
|
|||||||
if r.Type.Etype != TBLANK {
|
if r.Type.Etype != TBLANK {
|
||||||
aop = assignop(l.Type, r.Type, nil)
|
aop = assignop(l.Type, r.Type, nil)
|
||||||
if aop != 0 {
|
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))
|
Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(l.Type))
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
@ -618,7 +618,7 @@ OpSwitch:
|
|||||||
if l.Type.Etype != TBLANK {
|
if l.Type.Etype != TBLANK {
|
||||||
aop = assignop(r.Type, l.Type, nil)
|
aop = assignop(r.Type, l.Type, nil)
|
||||||
if aop != 0 {
|
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))
|
Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(op, 0), typekind(r.Type))
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
@ -657,7 +657,7 @@ OpSwitch:
|
|||||||
|
|
||||||
// okfor allows any array == array, map == map, func == func.
|
// okfor allows any array == array, map == map, func == func.
|
||||||
// restrict to slice/map/func == nil and nil == slice/map/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)
|
Yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type)
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
@ -681,11 +681,12 @@ OpSwitch:
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
var badtype *Type
|
if l.Type.IsStruct() {
|
||||||
if l.Type.IsStruct() && algtype1(l.Type, &badtype) == ANOEQ {
|
if f := l.Type.IncomparableField(); f != nil {
|
||||||
Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, badtype)
|
Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type)
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t = l.Type
|
t = l.Type
|
||||||
|
@ -3020,30 +3020,27 @@ func eqfor(t *Type, needsize *int) *Node {
|
|||||||
// a struct/array containing a non-memory field/element.
|
// a struct/array containing a non-memory field/element.
|
||||||
// Small memory is handled inline, and single non-memory
|
// Small memory is handled inline, and single non-memory
|
||||||
// is handled during type check (OCMPSTR etc).
|
// is handled during type check (OCMPSTR etc).
|
||||||
a := algtype1(t, nil)
|
switch a, _ := algtype1(t); a {
|
||||||
|
case AMEM:
|
||||||
if a != AMEM && a != -1 {
|
|
||||||
Fatalf("eqfor %v", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a == AMEM {
|
|
||||||
n := syslook("memequal")
|
n := syslook("memequal")
|
||||||
n = substArgTypes(n, t, t)
|
n = substArgTypes(n, t, t)
|
||||||
*needsize = 1
|
*needsize = 1
|
||||||
return n
|
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
|
||||||
}
|
}
|
||||||
|
Fatalf("eqfor %v", t)
|
||||||
sym := typesymprefix(".eq", t)
|
return nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The result of walkcompare MUST be assigned back to n, e.g.
|
// The result of walkcompare MUST be assigned back to n, e.g.
|
||||||
|
Loading…
Reference in New Issue
Block a user