}
return AINTER, nil
- case TARRAY:
- if t.IsSlice() {
- return ANOEQ, t
- }
+ case TSLICE:
+ return ANOEQ, t
+ case TARRAY:
a, bad := algtype1(t.Elem())
switch a {
case AMEM:
Fatalf("genhash %v", t)
case TARRAY:
- if t.IsSlice() {
- Fatalf("genhash %v", t)
- }
-
// An array of pure memory would be handled by the
// standard algorithm, so the element type must not be
// pure memory.
Fatalf("geneq %v", t)
case TARRAY:
- if t.IsSlice() {
- Fatalf("geneq %v", t)
- }
-
// An array of pure memory would be handled by the
// standard memequal, so the element type must not be
// pure memory. Even if we unrolled the range loop,
if t.Elem() == nil {
break
}
- if t.IsArray() {
- dowidth(t.Elem())
- if t.Elem().Width != 0 {
- cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
- if uint64(t.NumElem()) > cap {
- Yyerror("type %v larger than address space", Tconv(t, FmtLong))
- }
- }
-
- w = t.NumElem() * t.Elem().Width
- t.Align = t.Elem().Align
- } else if t.IsSlice() {
- w = int64(sizeof_Array)
- checkwidth(t.Elem())
- t.Align = uint8(Widthptr)
- } else if t.isDDDArray() {
+ if t.isDDDArray() {
if !t.Broke {
Yyerror("use of [...] array outside of array literal")
t.Broke = true
}
- } else {
- Fatalf("dowidth %v", t) // probably [...]T
+ break
+ }
+
+ dowidth(t.Elem())
+ if t.Elem().Width != 0 {
+ cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
+ if uint64(t.NumElem()) > cap {
+ Yyerror("type %v larger than address space", Tconv(t, FmtLong))
+ }
}
+ w = t.NumElem() * t.Elem().Width
+ t.Align = t.Elem().Align
+
+ case TSLICE:
+ if t.Elem() == nil {
+ break
+ }
+ w = int64(sizeof_Array)
+ checkwidth(t.Elem())
+ t.Align = uint8(Widthptr)
case TSTRUCT:
if t.IsFuncArgStruct() {
if t.isDDDArray() {
Fatalf("array bounds should be known at export time: %v", t)
}
- if t.IsArray() {
- p.tag(arrayTag)
- p.int64(t.NumElem())
- } else {
- p.tag(sliceTag)
- }
+ p.tag(arrayTag)
+ p.int64(t.NumElem())
+ p.typ(t.Elem())
+
+ case TSLICE:
+ p.tag(sliceTag)
p.typ(t.Elem())
case TDDDFIELD:
dclcontext = savedContext
- case arrayTag, sliceTag:
+ case arrayTag:
t = p.newtyp(TARRAY)
- var bound int64
- if i == arrayTag {
- bound = p.int64()
- }
+ bound := p.int64()
elem := p.typ()
- if i == arrayTag {
- t.Extra = &ArrayType{Elem: elem, Bound: bound}
- } else {
- t.Extra = SliceType{Elem: elem}
- }
+ t.Extra = &ArrayType{Elem: elem, Bound: bound}
+
+ case sliceTag:
+ t = p.newtyp(TSLICE)
+ elem := p.typ()
+ t.Extra = SliceType{Elem: elem}
case dddTag:
t = p.newtyp(TDDDFIELD)
return n
case TARRAY:
- if !t.IsSlice() {
- goto bad
- }
+ goto bad
case TPTR32,
TPTR64,
TMAP,
TCHAN,
TFUNC,
+ TSLICE,
TUNSAFEPTR:
break
t := n.Type
switch t.Etype {
- case TARRAY, TCHAN, TPTR32, TPTR64:
+ case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
if t.Sym == nil {
t = t.Elem()
}
case TMAP:
dumpexporttype(t.Val())
dumpexporttype(t.Key())
- case TARRAY, TCHAN, TPTR32, TPTR64:
+ case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
dumpexporttype(t.Elem())
}
TPTR64: "PTR64",
TFUNC: "FUNC",
TARRAY: "ARRAY",
+ TSLICE: "SLICE",
TSTRUCT: "STRUCT",
TCHAN: "CHAN",
TMAP: "MAP",
return "*" + t.Elem().String()
case TARRAY:
- if t.IsArray() {
- return fmt.Sprintf("[%d]%v", t.NumElem(), t.Elem())
- }
if t.isDDDArray() {
return "[...]" + t.Elem().String()
}
+ return fmt.Sprintf("[%d]%v", t.NumElem(), t.Elem())
+
+ case TSLICE:
return "[]" + t.Elem().String()
case TCHAN:
// Emit vardef if needed.
if nl.Op == ONAME {
switch nl.Type.Etype {
- case TARRAY, TSTRING, TINTER, TSTRUCT:
+ case TARRAY, TSLICE, TSTRING, TINTER, TSTRUCT:
Gvardef(nl)
}
}
return f(Ptrto(Types[TUINT8]), startOffset) &&
f(Types[Simtype[TUINT]], startOffset+int64(Widthptr))
- case TARRAY:
- if t.IsSlice() {
- return f(Ptrto(t.Elem()), startOffset+int64(Array_array)) &&
- f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) &&
- f(Types[Simtype[TUINT]], startOffset+int64(Array_cap))
- }
+ case TSLICE:
+ return f(Ptrto(t.Elem()), startOffset+int64(Array_array)) &&
+ f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) &&
+ f(Types[Simtype[TUINT]], startOffset+int64(Array_cap))
+ case TARRAY:
// Short-circuit [1e6]struct{}.
if t.Elem().Width == 0 {
return true
func Isfat(t *Type) bool {
if t != nil {
switch t.Etype {
- case TSTRUCT, TARRAY, TSTRING,
+ case TSTRUCT, TARRAY, TSLICE, TSTRING,
TINTER: // maybe remove later
return true
}
default:
Fatalf("orderstmt range %v", n.Type)
- case TARRAY:
+ case TARRAY, TSLICE:
if n.List.Len() < 2 || isblank(n.List.Second()) {
// for i := range x will only use x once, to compute len(x).
// No need to copy it.
bvset(bv, int32(*xoffset/int64(Widthptr)+1)) // pointer in second slot
*xoffset += t.Width
+ case TSLICE:
+ // struct { byte *array; uintgo len; uintgo cap; }
+ if *xoffset&int64(Widthptr-1) != 0 {
+ Fatalf("onebitwalktype1: invalid TARRAY alignment, %v", t)
+ }
+ bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot (BitsPointer)
+ *xoffset += t.Width
+
case TARRAY:
- if t.IsSlice() {
- // struct { byte *array; uintgo len; uintgo cap; }
- if *xoffset&int64(Widthptr-1) != 0 {
- Fatalf("onebitwalktype1: invalid TARRAY alignment, %v", t)
- }
- bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot (BitsPointer)
- *xoffset += t.Width
- } else {
- for i := int64(0); i < t.NumElem(); i++ {
- onebitwalktype1(t.Elem(), xoffset, bv)
- }
+ for i := int64(0); i < t.NumElem(); i++ {
+ onebitwalktype1(t.Elem(), xoffset, bv)
}
case TSTRUCT:
Yyerror("cannot range over %v", Nconv(n.Right, FmtLong))
goto out
- case TARRAY:
+ case TARRAY, TSLICE:
t1 = Types[TINT]
t2 = t.Elem()
default:
Fatalf("walkrange")
- case TARRAY:
+ case TARRAY, TSLICE:
if memclrrange(n, v1, v2, a) {
lineno = lno
return
tsym := t.Sym
if tsym == nil {
switch t.Etype {
- case TARRAY, TPTR32, TPTR64, TCHAN:
+ case TARRAY, TSLICE, TPTR32, TPTR64, TCHAN:
if t.Elem() != nil {
tsym = t.Elem().Sym
}
TCHAN: obj.KindChan,
TMAP: obj.KindMap,
TARRAY: obj.KindArray,
+ TSLICE: obj.KindArray,
TFUNC: obj.KindFunc,
TCOMPLEX64: obj.KindComplex64,
TCOMPLEX128: obj.KindComplex128,
TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL:
return false
- case TARRAY:
- if t.IsSlice() {
- return true
- }
+ case TSLICE:
+ return true
+ case TARRAY:
at := t.Extra.(*ArrayType)
if at.Haspointers != 0 {
return at.Haspointers-1 != 0
// struct { Type *type; void *data; }
return 2 * int64(Widthptr)
+ case TSLICE:
+ // struct { byte *array; uintgo len; uintgo cap; }
+ return int64(Widthptr)
+
case TARRAY:
- if t.IsSlice() {
- // struct { byte *array; uintgo len; uintgo cap; }
- return int64(Widthptr)
- }
// haspointers already eliminated t.NumElem() == 0.
return (t.NumElem()-1)*t.Elem().Width + typeptrdata(t.Elem())
return false
case TARRAY:
- if t.IsSlice() {
- Fatalf("slice can't be a map key: %v", t)
- }
return isreflexive(t.Elem())
case TSTRUCT:
return true
case TARRAY:
- if t.IsSlice() {
- Fatalf("slice can't be a map key: %v", t)
- }
return needkeyupdate(t.Elem())
case TSTRUCT:
ot = dextratype(s, ot, t, 0)
case TARRAY:
- if t.IsArray() {
- // ../../../../runtime/type.go:/arrayType
- s1 := dtypesym(t.Elem())
- t2 := typSlice(t.Elem())
- s2 := dtypesym(t2)
- ot = dcommontype(s, ot, t)
- ot = dsymptr(s, ot, s1, 0)
- ot = dsymptr(s, ot, s2, 0)
- ot = duintptr(s, ot, uint64(t.NumElem()))
- } else {
- // ../../../../runtime/type.go:/sliceType
- s1 := dtypesym(t.Elem())
+ // ../../../../runtime/type.go:/arrayType
+ s1 := dtypesym(t.Elem())
+ t2 := typSlice(t.Elem())
+ s2 := dtypesym(t2)
+ ot = dcommontype(s, ot, t)
+ ot = dsymptr(s, ot, s1, 0)
+ ot = dsymptr(s, ot, s2, 0)
+ ot = duintptr(s, ot, uint64(t.NumElem()))
+ ot = dextratype(s, ot, t, 0)
- ot = dcommontype(s, ot, t)
- ot = dsymptr(s, ot, s1, 0)
- }
+ case TSLICE:
+ // ../../../../runtime/type.go:/sliceType
+ s1 := dtypesym(t.Elem())
+ ot = dcommontype(s, ot, t)
+ ot = dsymptr(s, ot, s1, 0)
ot = dextratype(s, ot, t, 0)
- // ../../../../runtime/type.go:/chanType
case TCHAN:
+ // ../../../../runtime/type.go:/chanType
s1 := dtypesym(t.Elem())
-
ot = dcommontype(s, ot, t)
ot = dsymptr(s, ot, s1, 0)
ot = duintptr(s, ot, uint64(t.ChanDir()))
// functions must return the existing type structure rather
// than creating a new one.
switch t.Etype {
- case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSTRUCT:
+ case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRUCT:
keep = true
}
}
p.w.Ptr(offset / int64(Widthptr))
p.w.Ptr(offset/int64(Widthptr) + 1)
+ case TSLICE:
+ p.w.Ptr(offset / int64(Widthptr))
+
case TARRAY:
- if t.IsSlice() {
- p.w.Ptr(offset / int64(Widthptr))
- return
- }
if t.NumElem() == 0 {
// should have been handled by haspointers check above
Fatalf("GCProg.emit: empty array")
structlit(ctxt, 3, n, var_, init)
case OARRAYLIT:
- if t.Etype != TARRAY {
- Fatalf("anylit: not array")
- }
if t.IsSlice() {
slicelit(ctxt, n, var_, init)
break
}
+ if !t.IsArray() {
+ Fatalf("anylit: not array")
+ }
if var_.isSimpleName() && n.List.Len() > 4 {
if ctxt == 0 {
}
// nr is the array being converted to a slice
- if nr.Type == nil || nr.Type.Etype != TARRAY || nr.Type.IsSlice() {
+ if nr.Type == nil || !nr.Type.IsArray() {
return false
}
{ChanArgsType{}, 4, 8},
{PtrType{}, 4, 8},
{SliceType{}, 4, 8},
- {DDDArrayType{}, 4, 8},
}
for _, tt := range tests {
opAndType{OEQ, TINT64}: ssa.OpEq64,
opAndType{OEQ, TUINT64}: ssa.OpEq64,
opAndType{OEQ, TINTER}: ssa.OpEqInter,
- opAndType{OEQ, TARRAY}: ssa.OpEqSlice,
+ opAndType{OEQ, TSLICE}: ssa.OpEqSlice,
opAndType{OEQ, TFUNC}: ssa.OpEqPtr,
opAndType{OEQ, TMAP}: ssa.OpEqPtr,
opAndType{OEQ, TCHAN}: ssa.OpEqPtr,
opAndType{ONE, TINT64}: ssa.OpNeq64,
opAndType{ONE, TUINT64}: ssa.OpNeq64,
opAndType{ONE, TINTER}: ssa.OpNeqInter,
- opAndType{ONE, TARRAY}: ssa.OpNeqSlice,
+ opAndType{ONE, TSLICE}: ssa.OpNeqSlice,
opAndType{ONE, TFUNC}: ssa.OpNeqPtr,
opAndType{ONE, TMAP}: ssa.OpNeqPtr,
opAndType{ONE, TCHAN}: ssa.OpNeqPtr,
}
switch t.Etype {
case TARRAY:
- if t.IsSlice() {
- return true
- }
// We can't do arrays because dynamic indexing is
// not supported on SSA variables.
// TODO: maybe allow if length is <=1? All indexes
case TSTRUCT,
TARRAY,
+ TSLICE,
TMAP,
TCHAN,
TSTRING,
if t1 == t2 {
return true
}
- if t1 == nil || t2 == nil || t1.Etype != t2.Etype {
+ if t1 == nil || t2 == nil || t1.Etype != t2.Etype || t1.Broke || t2.Broke {
return false
}
if t1.Sym != nil || t2.Sym != nil {
// 5. src is the predeclared identifier nil and dst is a nillable type.
if src.Etype == TNIL {
switch dst.Etype {
- case TARRAY:
- if !dst.IsSlice() {
- break
- }
- fallthrough
-
case TPTR32,
TPTR64,
TFUNC,
TMAP,
TCHAN,
- TINTER:
+ TINTER,
+ TSLICE:
return OCONVNOP
}
}
TPTR64
TFUNC
+ TSLICE
TARRAY
TSTRUCT
TCHAN
NTYPE
)
-const (
- sliceBound = -1 // slices have Bound=sliceBound
- dddBound = -100 // arrays declared as [...]T start life with Bound=dddBound
-)
-
// ChanDir is whether a channel can send, receive, or both.
type ChanDir uint8
// TCHANARGS: ChanArgsType
// TCHAN: *ChanType
// TPTR32, TPTR64: PtrType
- // TARRAY: *ArrayType, SliceType, or DDDArrayType
+ // TARRAY: *ArrayType
+ // TSLICE: SliceType
Extra interface{}
// Width is the width of this Type in bytes.
return t.Extra.(*ChanType)
}
-// ArrayType contains Type fields specific to array types with known lengths.
+// ArrayType contains Type fields specific to array types.
type ArrayType struct {
Elem *Type // element type
- Bound int64 // number of elements; always >= 0; do not use with sliceBound or dddBound
+ Bound int64 // number of elements; <0 if unknown yet
Haspointers uint8 // 0 unknown, 1 no, 2 yes
}
Elem *Type // element type
}
-// DDDArrayType contains Type fields specific to ddd array types.
-type DDDArrayType struct {
- Elem *Type // element type
-}
-
// A Field represents a field in a struct or a method in an interface or
// associated with a named type.
type Field struct {
// typArray returns a new fixed-length array Type.
func typArray(elem *Type, bound int64) *Type {
+ if bound < 0 {
+ Fatalf("typArray: invalid bound %v", bound)
+ }
t := typ(TARRAY)
t.Extra = &ArrayType{Elem: elem, Bound: bound}
return t
// typSlice returns a new slice Type.
func typSlice(elem *Type) *Type {
- t := typ(TARRAY)
+ t := typ(TSLICE)
t.Extra = SliceType{Elem: elem}
return t
}
// typDDDArray returns a new [...]T array Type.
func typDDDArray(elem *Type) *Type {
t := typ(TARRAY)
- t.Extra = DDDArrayType{Elem: elem}
+ t.Extra = &ArrayType{Elem: elem, Bound: -1}
return t
}
elem := substAny(t.Elem(), types)
if elem != t.Elem() {
t = t.Copy()
- switch x := t.Extra.(type) {
- case *ArrayType:
- x.Elem = elem
- case SliceType:
- t.Extra = SliceType{Elem: elem}
- case DDDArrayType:
- t.Extra = DDDArrayType{Elem: elem}
- default:
- Fatalf("substAny bad array elem type %T %v", x, t)
- }
+ t.Extra.(*ArrayType).Elem = elem
+ }
+
+ case TSLICE:
+ elem := substAny(t.Elem(), types)
+ if elem != t.Elem() {
+ t = t.Copy()
+ t.Extra = SliceType{Elem: elem}
}
case TCHAN:
x := *t.Extra.(*ChanType)
nt.Extra = &x
case TARRAY:
- if arr, ok := t.Extra.(*ArrayType); ok {
- x := *arr
- nt.Extra = &x
- }
+ x := *t.Extra.(*ArrayType)
+ nt.Extra = &x
}
// TODO(mdempsky): Find out why this is necessary and explain.
if t.Orig == t {
case TPTR32, TPTR64:
return t.Extra.(PtrType).Elem
case TARRAY:
- switch t := t.Extra.(type) {
- case *ArrayType:
- return t.Elem
- case SliceType:
- return t.Elem
- case DDDArrayType:
- return t.Elem
- }
+ return t.Extra.(*ArrayType).Elem
+ case TSLICE:
+ return t.Extra.(SliceType).Elem
case TCHAN:
return t.Extra.(*ChanType).Elem
}
if t.Etype != TARRAY {
return false
}
- _, ok := t.Extra.(DDDArrayType)
- return ok
+ return t.Extra.(*ArrayType).Bound < 0
}
// ArgWidth returns the total aligned argument size for a function.
}
return t.Val().cmp(x.Val())
- case TPTR32, TPTR64:
- // No special cases for these two, they are handled
+ case TPTR32, TPTR64, TSLICE:
+ // No special cases for these, they are handled
// by the general code after the switch.
case TSTRUCT:
panic(e)
}
- // Common element type comparison for TARRAY, TCHAN, TPTR32, and TPTR64.
+ // Common element type comparison for TARRAY, TCHAN, TPTR32, TPTR64, and TSLICE.
return t.Elem().cmp(x.Elem())
}
return t.Etype == TCHAN
}
-// TODO: Remove noinline when issue 15084 is resolved.
-//go:noinline
func (t *Type) IsSlice() bool {
- if t.Etype != TARRAY {
- return false
- }
- _, ok := t.Extra.(SliceType)
- return ok
+ return t.Etype == TSLICE
}
func (t *Type) IsArray() bool {
- if t.Etype != TARRAY {
- return false
- }
- _, ok := t.Extra.(*ArrayType)
- return ok
+ return t.Etype == TARRAY
}
func (t *Type) IsStruct() bool {
func (t *Type) NumElem() int64 {
t.wantEtype(TARRAY)
- switch t := t.Extra.(type) {
- case *ArrayType:
- return t.Bound
- case SliceType:
- return sliceBound
- case DDDArrayType:
- return dddBound
+ at := t.Extra.(*ArrayType)
+ if at.Bound < 0 {
+ Fatalf("NumElem array %v does not have bound yet", t)
}
- Fatalf("NumElem on non-array %T %v", t.Extra, t)
- return 0
+ return at.Bound
}
// SetNumElem sets the number of elements in an array type.
-// It should not be used if at all possible.
-// Create a new array/slice/dddArray with typX instead.
-// The only allowed uses are:
-// * array -> slice as a hack to suppress extra error output
-// * ddd array -> array
-// TODO(josharian): figure out how to get rid of this entirely.
+// The only allowed use is on array types created with typDDDArray.
+// For other uses, create a new array with typArray instead.
func (t *Type) SetNumElem(n int64) {
t.wantEtype(TARRAY)
- switch {
- case n >= 0:
- if !t.isDDDArray() {
- Fatalf("SetNumElem non-ddd -> array %v", t)
- }
- t.Extra = &ArrayType{Elem: t.Elem(), Bound: n}
- case n == sliceBound:
- if !t.IsArray() {
- Fatalf("SetNumElem non-array -> slice %v", t)
- }
- t.Extra = SliceType{Elem: t.Elem()}
- default:
- Fatalf("SetNumElem %d %v", n, t)
+ at := t.Extra.(*ArrayType)
+ if at.Bound >= 0 {
+ Fatalf("SetNumElem array %v already has bound %d", t, at.Bound)
}
+ at.Bound = n
}
// ChanDir returns the direction of a channel type t.
TCHAN: "chan",
TMAP: "map",
TARRAY: "array",
+ TSLICE: "slice",
TFUNC: "func",
TNIL: "nil",
TIDEAL: "untyped number",
n.Type = nil
return n
- case TSTRING, TARRAY:
+ case TSTRING, TARRAY, TSLICE:
n.Right = indexlit(n.Right)
if t.IsString() {
n.Type = bytetype
n.Type = t.Elem()
}
why := "string"
- if t.Etype == TARRAY {
- if t.IsArray() {
- why = "array"
- } else {
- why = "slice"
- }
+ if t.IsArray() {
+ why = "array"
+ } else if t.IsSlice() {
+ why = "slice"
}
if n.Right.Type != nil && !n.Right.Type.IsInteger() {
}
case TARRAY:
- if t.IsSlice() {
- break
- }
if callrecv(l) { // has call or receive
break
}
n.Type = nil
return n
- case TARRAY:
- if !t.IsSlice() {
- Yyerror("cannot make type %v", t)
- n.Type = nil
- return n
- }
-
+ case TSLICE:
if i >= len(args) {
Yyerror("missing len argument to make(%v)", t)
n.Type = nil
hash[v] = n
}
+// iscomptype reports whether type t is a composite literal type
+// or a pointer to one.
func iscomptype(t *Type) bool {
+ if t.IsPtr() {
+ t = t.Elem()
+ }
+
switch t.Etype {
- case TARRAY, TSTRUCT, TMAP:
+ case TARRAY, TSLICE, TSTRUCT, TMAP:
return true
-
- case TPTR32, TPTR64:
- switch t.Elem().Etype {
- case TARRAY, TSTRUCT, TMAP:
- return true
- }
+ default:
+ return false
}
-
- return false
}
func pushtype(n *Node, t *Type) {
Yyerror("invalid type for composite literal: %v", t)
n.Type = nil
- case TARRAY:
+ case TARRAY, TSLICE:
// Only allocate hash if there are some key/value pairs.
var hash map[int64]*Node
for _, n1 := range n.List.Slice() {
}
length := int64(0)
i := 0
+ checkBounds := t.IsArray() && !t.isDDDArray()
for i2, n2 := range n.List.Slice() {
l := n2
setlineno(l)
i++
if int64(i) > length {
length = int64(i)
- if t.IsArray() && length > t.NumElem() {
+ if checkBounds && length > t.NumElem() {
setlineno(l)
Yyerror("array index %d out of bounds [0:%d]", length-1, t.NumElem())
- // suppress any further errors out of bounds errors for the same type by pretending it is a slice
- t.SetNumElem(sliceBound)
+ checkBounds = false
}
}
okforcap[TARRAY] = true
okforcap[TCHAN] = true
+ okforcap[TSLICE] = true
okforconst[TBOOL] = true
okforconst[TSTRING] = true
okforlen[TARRAY] = true
okforlen[TCHAN] = true
okforlen[TMAP] = true
+ okforlen[TSLICE] = true
okforlen[TSTRING] = true
okforeq[TPTR32] = true
okforeq[TBOOL] = true
okforeq[TMAP] = true // nil only; refined in typecheck
okforeq[TFUNC] = true // nil only; refined in typecheck
- okforeq[TARRAY] = true // nil slice only; refined in typecheck
- okforeq[TSTRUCT] = true // it's complicated; refined in typecheck
+ okforeq[TSLICE] = true // nil only; refined in typecheck
+ okforeq[TARRAY] = true // only if element type is comparable; refined in typecheck
+ okforeq[TSTRUCT] = true // only if all struct fields are comparable; refined in typecheck
okforcmp[TSTRING] = true
default:
return n
- case TARRAY:
- if t.IsSlice() {
- return n
- }
-
- case TSTRUCT:
+ case TARRAY, TSTRUCT:
break
}