Commit 196b6630 authored by Russ Cox's avatar Russ Cox

gc: implement == on structs and arrays

To allow these types as map keys, we must fill in
equal and hash functions in their algorithm tables.
Structs or arrays that are "just memory", like [2]int,
can and do continue to use the AMEM algorithm.
Structs or arrays that contain special values like
strings or interface values use generated functions
for both equal and hash.

The runtime helper func runtime.equal(t, x, y) bool handles
the general equality case for x == y and calls out to
the equal implementation in the algorithm table.

For short values (<= 4 struct fields or array elements),
the sequence of elementwise comparisons is inlined
instead of calling runtime.equal.

R=ken, mpimenov
CC=golang-dev
https://golang.org/cl/5451105
parent 83f648c9
......@@ -43,6 +43,8 @@ struct Prog
uchar scond;
};
#define TEXTFLAG reg
#define REGALLOC_R0 0
#define REGALLOC_RMAX REGEXT
#define REGALLOC_F0 (REGALLOC_RMAX+1)
......
......@@ -585,6 +585,10 @@ loop:
errorexit();
}
cursym = s;
if(s->type != 0 && s->type != SXREF && (p->reg & DUPOK)) {
skip = 1;
goto casedef;
}
if(ntext++ == 0 && s->type != 0 && s->type != SXREF) {
/* redefinition, so file has probably been seen before */
if(debug['v'])
......@@ -592,13 +596,8 @@ loop:
return;
}
skip = 0;
if(s->type != 0 && s->type != SXREF) {
if(p->reg & DUPOK) {
skip = 1;
goto casedef;
}
if(s->type != 0 && s->type != SXREF)
diag("redefinition: %s\n%P", s->name, p);
}
if(etextp)
etextp->next = s;
else
......
......@@ -41,6 +41,8 @@ struct Prog
void* reg; // pointer to containing Reg struct
};
#define TEXTFLAG from.scale
EXTERN int32 dynloc;
EXTERN uchar reg[D_NONE];
EXTERN int32 pcloc; // instruction counter
......
......@@ -592,6 +592,10 @@ loop:
case ATEXT:
s = p->from.sym;
if(s->text != nil) {
if(p->from.scale & DUPOK) {
skip = 1;
goto casdef;
}
diag("%s: %s: redefinition", pn, s->name);
return;
}
......
......@@ -43,6 +43,8 @@ struct Prog
void* reg; // pointer to containing Reg struct
};
#define TEXTFLAG from.scale
// foptoas flags
enum
{
......
......@@ -600,6 +600,10 @@ loop:
case ATEXT:
s = p->from.sym;
if(s->text != nil) {
if(p->from.scale & DUPOK) {
skip = 1;
goto casdef;
}
diag("%s: %s: redefinition", pn, s->name);
return;
}
......
......@@ -497,6 +497,7 @@ typeinit(void)
okforeq[TMAP] = 1; // nil only; refined in typecheck
okforeq[TFUNC] = 1; // nil only; refined in typecheck
okforeq[TARRAY] = 1; // nil slice only; refined in typecheck
okforeq[TSTRUCT] = 1; // it's complicated; refined in typecheck
okforcmp[TSTRING] = 1;
......
This diff is collapsed.
......@@ -37,23 +37,24 @@ enum
AUNK = 100,
// these values are known by runtime
// These values are known by runtime.
// The MEMx and NOEQx values must run in parallel. See algtype.
AMEM = 0,
ANOEQ,
ASTRING,
AINTER,
ANILINTER,
ASLICE,
AMEM8,
AMEM16,
AMEM32,
AMEM64,
AMEM128,
ANOEQ,
ANOEQ8,
ANOEQ16,
ANOEQ32,
ANOEQ64,
ANOEQ128,
ASTRING,
AINTER,
ANILINTER,
ASLICE,
BADWIDTH = -1000000000,
};
......@@ -245,6 +246,7 @@ struct Node
uchar readonly;
uchar implicit; // don't show in printout
uchar addrtaken; // address taken, even if not moved to heap
uchar dupok; // duplicate definitions ok (for func)
// most nodes
Type* type;
......@@ -1085,6 +1087,7 @@ void dumptypestructs(void);
Type* methodfunc(Type *f, Type*);
Node* typename(Type *t);
Sym* typesym(Type *t);
Sym* typesymprefix(char *prefix, Type *t);
int haspointers(Type *t);
/*
......@@ -1109,6 +1112,7 @@ Node* adddot(Node *n);
int adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase);
Type* aindex(Node *b, Type *t);
int algtype(Type *t);
int algtype1(Type *t, Type **bad);
void argtype(Node *on, Type *t);
Node* assignconv(Node *n, Type *t, char *context);
int assignop(Type *src, Type *dst, char **why);
......@@ -1129,6 +1133,8 @@ void frame(int context);
Type* funcfirst(Iter *s, Type *t);
Type* funcnext(Iter *s);
void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface);
void genhash(Sym *sym, Type *t);
void geneq(Sym *sym, Type *t);
Type** getinarg(Type *t);
Type* getinargx(Type *t);
Type** getoutarg(Type *t);
......@@ -1237,6 +1243,7 @@ void walkexprlist(NodeList *l, NodeList **init);
void walkexprlistsafe(NodeList *l, NodeList **init);
void walkstmt(Node **np);
void walkstmtlist(NodeList *l);
Node* conv(Node*, Type*);
/*
* arch-specific ggen.c/gsubr.c/gobj.c/pgen.c
......
......@@ -7,6 +7,8 @@
#include "gg.h"
#include "opt.h"
#define DUPOK (1<<1) /* same in all architectures */
static void allocauto(Prog* p);
void
......@@ -70,6 +72,8 @@ compile(Node *fn)
nodconst(&nod1, types[TINT32], 0);
ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
if(fn->dupok)
ptxt->TEXTFLAG = DUPOK;
afunclit(&ptxt->from);
ginit();
......
......@@ -13,6 +13,7 @@
static NodeList* signatlist;
static Sym* dtypesym(Type*);
static Sym* weaktypesym(Type*);
static Sym* dalgsym(Type*);
static int
sigcmp(Sig *a, Sig *b)
......@@ -553,17 +554,20 @@ haspointers(Type *t)
static int
dcommontype(Sym *s, int ot, Type *t)
{
int i, sizeofAlg;
Sym *sptr;
int i, alg, sizeofAlg;
Sym *sptr, *algsym;
static Sym *algarray;
char *p;
sizeofAlg = 4*widthptr;
if(algarray == nil)
algarray = pkglookup("algarray", runtimepkg);
alg = algtype(t);
algsym = S;
if(alg < 0)
algsym = dalgsym(t);
dowidth(t);
if(t->sym != nil && !isptr[t->etype])
sptr = dtypesym(ptrto(t));
else
......@@ -600,7 +604,10 @@ dcommontype(Sym *s, int ot, Type *t)
if(!haspointers(t))
i |= KindNoPointers;
ot = duint8(s, ot, i); // kind
ot = dsymptr(s, ot, algarray, algtype(t)*sizeofAlg);
if(alg >= 0)
ot = dsymptr(s, ot, algarray, alg*sizeofAlg);
else
ot = dsymptr(s, ot, algsym, 0);
p = smprint("%-uT", t);
//print("dcommontype: %s\n", p);
ot = dgostringptr(s, ot, p); // string
......@@ -629,6 +636,19 @@ typesym(Type *t)
return s;
}
Sym*
typesymprefix(char *prefix, Type *t)
{
char *p;
Sym *s;
p = smprint("%s.%-T", prefix, t);
s = pkglookup(p, typepkg);
//print("algsym: %s -> %+S\n", p, s);
free(p);
return s;
}
Node*
typename(Type *t)
{
......@@ -930,3 +950,43 @@ dumptypestructs(void)
dimportpath(mkpkg(strlit("main")));
}
}
Sym*
dalgsym(Type *t)
{
int ot;
Sym *s, *hash, *eq;
char buf[100];
// dalgsym is only called for a type that needs an algorithm table,
// which implies that the type is comparable (or else it would use ANOEQ).
s = typesymprefix(".alg", t);
hash = typesymprefix(".hash", t);
genhash(hash, t);
eq = typesymprefix(".eq", t);
geneq(eq, t);
// ../../pkg/runtime/runtime.h:/Alg
ot = 0;
ot = dsymptr(s, ot, hash, 0);
ot = dsymptr(s, ot, eq, 0);
ot = dsymptr(s, ot, pkglookup("memprint", runtimepkg), 0);
switch(t->width) {
default:
ot = dsymptr(s, ot, pkglookup("memcopy", runtimepkg), 0);
break;
case 1:
case 2:
case 4:
case 8:
case 16:
snprint(buf, sizeof buf, "memcopy%d", (int)t->width*8);
ot = dsymptr(s, ot, pkglookup(buf, runtimepkg), 0);
break;
}
ggloblsym(s, ot, 1);
return s;
}
......@@ -8,6 +8,8 @@
package PACKAGE
import "unsafe"
// emitted by compiler, not referred to by go programs
func new(typ *byte) *any
......@@ -80,6 +82,8 @@ func efaceeq(i1 any, i2 any) (ret bool)
func ifacethash(i1 any) (ret uint32)
func efacethash(i1 any) (ret uint32)
func equal(typ *byte, x1, x2 any) (ret bool)
// *byte is really *runtime.Type
func makemap(mapType *byte, hint int64) (hmap map[any]any)
func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any)
......@@ -119,6 +123,13 @@ func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary [
func closure() // has args, but compiler fills in
func memequal(eq *bool, size uintptr, x, y unsafe.Pointer)
func memequal8(eq *bool, size uintptr, x, y unsafe.Pointer)
func memequal16(eq *bool, size uintptr, x, y unsafe.Pointer)
func memequal32(eq *bool, size uintptr, x, y unsafe.Pointer)
func memequal64(eq *bool, size uintptr, x, y unsafe.Pointer)
func memequal128(eq *bool, size uintptr, x, y unsafe.Pointer)
// only used on 32-bit
func int64div(int64, int64) int64
func uint64div(uint64, uint64) uint64
......
This diff is collapsed.
......@@ -90,11 +90,15 @@ static char* _typekind[] = {
};
static char*
typekind(int et)
typekind(Type *t)
{
int et;
static char buf[50];
char *s;
if(isslice(t))
return "slice";
et = t->etype;
if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil)
return s;
snprint(buf, sizeof buf, "etype=%d", et);
......@@ -113,7 +117,7 @@ typecheck(Node **np, int top)
Node *n, *l, *r;
NodeList *args;
int lno, ok, ntop;
Type *t, *tp, *ft, *missing, *have;
Type *t, *tp, *ft, *missing, *have, *badtype;
Val v;
char *why;
......@@ -419,15 +423,25 @@ reswitch:
if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) {
// comparison is okay as long as one side is
// assignable to the other. convert so they have
// the same type. (the only conversion that isn't
// a no-op is concrete == interface.)
// the same type.
//
// the only conversion that isn't a no-op is concrete == interface.
// in that case, check comparability of the concrete type.
if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) {
if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) {
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type));
goto error;
}
l = nod(aop, l, N);
l->type = r->type;
l->typecheck = 1;
n->left = l;
t = l->type;
} else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) {
if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) {
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type));
goto error;
}
r = nod(aop, r, N);
r->type = l->type;
r->typecheck = 1;
......@@ -442,16 +456,15 @@ reswitch:
goto error;
}
if(!okfor[op][et]) {
notokfor:
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(et));
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t));
goto error;
}
// okfor allows any array == array, map == map, func == func.
// restrict to slice/map/func == nil and nil == slice/map/func.
if(l->type->etype == TARRAY && !isslice(l->type))
goto notokfor;
if(r->type->etype == TARRAY && !isslice(r->type))
goto notokfor;
if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) {
yyerror("invalid operation: %N (%T cannot be compared)", n, l->type);
goto error;
}
if(isslice(l->type) && !isnil(l) && !isnil(r)) {
yyerror("invalid operation: %N (slice can only be compared to nil)", n);
goto error;
......@@ -464,6 +477,10 @@ reswitch:
yyerror("invalid operation: %N (func can only be compared to nil)", n);
goto error;
}
if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) {
yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype);
goto error;
}
t = l->type;
if(iscmp[n->op]) {
......
......@@ -7,7 +7,6 @@
#include "go.h"
static Node* walkprint(Node*, NodeList**, int);
static Node* conv(Node*, Type*);
static Node* mapfn(char*, Type*);
static Node* mapfndel(char*, Type*);
static Node* ascompatee1(int, Node*, Node*, NodeList**);
......@@ -22,6 +21,7 @@ static NodeList* reorder3(NodeList*);
static Node* addstr(Node*, NodeList**);
static Node* appendslice(Node*, NodeList**);
static Node* append(Node*, NodeList**);
static void walkcompare(Node**, NodeList**);
// can this code branch reach the end
// without an unconditional RETURN
......@@ -456,8 +456,6 @@ walkexpr(Node **np, NodeList **init)
case OXOR:
case OSUB:
case OMUL:
case OEQ:
case ONE:
case OLT:
case OLE:
case OGE:
......@@ -468,6 +466,13 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->right, init);
goto ret;
case OEQ:
case ONE:
walkexpr(&n->left, init);
walkexpr(&n->right, init);
walkcompare(&n, init);
goto ret;
case OANDAND:
case OOROR:
walkexpr(&n->left, init);
......@@ -2212,7 +2217,7 @@ mkcall1(Node *fn, Type *t, NodeList **init, ...)
return r;
}
static Node*
Node*
conv(Node *n, Type *t)
{
if(eqtype(n->type, t))
......@@ -2386,3 +2391,182 @@ append(Node *n, NodeList **init)
*init = concat(*init, l);
return ns;
}
static Node*
eqfor(Type *t)
{
int a;
Node *n;
Node *ntype;
Sym *sym;
// Should only arrive here with large memory or
// 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)
fatal("eqfor %T", t);
if(a == AMEM)
return syslook("memequal", 0);
sym = typesymprefix(".eq", t);
n = newname(sym);
n->class = PFUNC;
ntype = nod(OTFUNC, N, N);
ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(types[TBOOL]))));
ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUNSAFEPTR])));
ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUNSAFEPTR])));
typecheck(&ntype, Etype);
n->type = ntype->type;
return n;
}
static int
countfield(Type *t)
{
Type *t1;
int n;
n = 0;
for(t1=t->type; t1!=T; t1=t1->down)
n++;
return n;
}
static void
walkcompare(Node **np, NodeList **init)
{
Node *n, *l, *r, *fn, *call, *a, *li, *ri, *expr;
int andor, i;
Type *t, *t1;
static Node *tempbool;
n = *np;
// Must be comparison of array or struct.
// Otherwise back end handles it.
t = n->left->type;
switch(t->etype) {
default:
return;
case TARRAY:
if(isslice(t))
return;
break;
case TSTRUCT:
break;
}
if(!islvalue(n->left) || !islvalue(n->right))
goto hard;
l = temp(ptrto(t));
a = nod(OAS, l, nod(OADDR, n->left, N));
a->right->etype = 1; // addr does not escape
typecheck(&a, Etop);
*init = list(*init, a);
r = temp(ptrto(t));
a = nod(OAS, r, nod(OADDR, n->right, N));
a->right->etype = 1; // addr does not escape
typecheck(&a, Etop);
*init = list(*init, a);
expr = N;
andor = OANDAND;
if(n->op == ONE)
andor = OOROR;
if(t->etype == TARRAY &&
t->bound <= 4 &&
issimple[t->type->etype]) {
// Four or fewer elements of a basic type.
// Unroll comparisons.
for(i=0; i<t->bound; i++) {
li = nod(OINDEX, l, nodintconst(i));
ri = nod(OINDEX, r, nodintconst(i));
a = nod(n->op, li, ri);
if(expr == N)
expr = a;
else
expr = nod(andor, expr, a);
}
if(expr == N)
expr = nodbool(n->op == OEQ);
typecheck(&expr, Erv);
walkexpr(&expr, init);
*np = expr;
return;
}
if(t->etype == TSTRUCT && countfield(t) <= 4) {
// Struct of four or fewer fields.
// Inline comparisons.
for(t1=t->type; t1; t1=t1->down) {
li = nod(OXDOT, l, newname(t1->sym));
ri = nod(OXDOT, r, newname(t1->sym));
a = nod(n->op, li, ri);
if(expr == N)
expr = a;
else
expr = nod(andor, expr, a);
}
if(expr == N)
expr = nodbool(n->op == OEQ);
typecheck(&expr, Erv);
walkexpr(&expr, init);
*np = expr;
return;
}
// Chose not to inline, but still have addresses.
// Call equality function directly.
// The equality function requires a bool pointer for
// storing its address, because it has to be callable
// from C, and C can't access an ordinary Go return value.
// To avoid creating many temporaries, cache one per function.
if(tempbool == N || tempbool->curfn != curfn)
tempbool = temp(types[TBOOL]);
call = nod(OCALL, eqfor(t), N);
a = nod(OADDR, tempbool, N);
a->etype = 1; // does not escape
call->list = list(call->list, a);
call->list = list(call->list, nodintconst(t->width));
call->list = list(call->list, conv(l, types[TUNSAFEPTR]));
call->list = list(call->list, conv(r, types[TUNSAFEPTR]));
typecheck(&call, Etop);
walkstmt(&call);
*init = list(*init, call);
if(n->op == OEQ)
r = tempbool;
else
r = nod(ONOT, tempbool, N);
typecheck(&r, Erv);
walkexpr(&r, init);
*np = r;
return;
hard:
// Cannot take address of one or both of the operands.
// Instead, pass directly to runtime helper function.
// Easier on the stack than passing the address
// of temporary variables, because we are better at reusing
// the argument space than temporary variable space.
fn = syslook("equal", 1);
l = n->left;
r = n->right;
argtype(fn, n->left->type);
argtype(fn, n->left->type);
r = mkcall1(fn, n->type, init, typename(n->left->type), l, r);
if(n->op == ONE) {
r = nod(ONOT, r, N);
typecheck(&r, Erv);
}
*np = r;
return;
}
......@@ -1439,7 +1439,7 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype)
if (strncmp(s, "go.string.", 10) == 0)
return;
if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0) {
if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0 && strncmp(s, "type..", 6) != 0) {
defgotype(sym);
return;
}
......
......@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
#include "type.h"
/*
* map and chan helpers for
......@@ -68,7 +69,7 @@ runtime·memprint(uintptr s, void *a)
v = *(uint16*)a;
break;
case 4:
v = *(uintptr*)a;
v = *(uint32*)a;
break;
case 8:
v = *(uint64*)a;
......@@ -343,3 +344,18 @@ runtime·algarray[] =
[ANOEQ128] { runtime·nohash, runtime·noequal, runtime·memprint, runtime·memcopy128 },
};
// Runtime helpers.
// func equal(t *Type, x T, y T) (ret bool)
#pragma textflag 7
void
runtime·equal(Type *t, ...)
{
byte *x, *y;
bool *ret;
x = (byte*)(&t+1);
y = x + t->size;
ret = (bool*)(y + t->size);
t->alg->equal(ret, t->size, x, y);
}
......@@ -66,7 +66,7 @@
#define malloc runtime·mal
#define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c))
#define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c))
#define assert(a) if(!(a)) runtime·throw("assert")
#define assert(a) if(!(a)) runtime·throw("hashmap assert")
#define free(x) runtime·free(x)
#define memmove(a,b,c) runtime·memmove(a, b, c)
......
......@@ -358,21 +358,21 @@ enum {
enum
{
AMEM,
ANOEQ,
ASTRING,
AINTER,
ANILINTER,
ASLICE,
AMEM8,
AMEM16,
AMEM32,
AMEM64,
AMEM128,
ANOEQ,
ANOEQ8,
ANOEQ16,
ANOEQ32,
ANOEQ64,
ANOEQ128,
ASTRING,
AINTER,
ANILINTER,
ASLICE,
Amax
};
typedef struct Alg Alg;
......
......@@ -57,18 +57,26 @@ func main() {
isfalse(ib == id)
istrue(ic == id)
istrue(ie == ie)
// these are okay because one side of the
// comparison need only be assignable to the other.
isfalse(a == ib)
isfalse(a == ic)
isfalse(a == id)
isfalse(b == ic)
isfalse(b == id)
istrue(ia != ib)
istrue(ia != ic)
istrue(ia != id)
istrue(ib != ic)
istrue(ib != id)
isfalse(ic != id)
isfalse(ie != ie)
// these are not okay, because there is no comparison on slices or maps.
//isfalse(a == ib)
//isfalse(a == ic)
//isfalse(a == id)
//isfalse(b == ic)
//isfalse(b == id)
istrue(c == id)
istrue(e == ie)
isfalse(ia == b)
//isfalse(ia == b)
isfalse(ia == c)
isfalse(ia == d)
isfalse(ib == c)
......@@ -76,24 +84,40 @@ func main() {
istrue(ic == d)
istrue(ie == e)
//istrue(a != ib)
//istrue(a != ic)
//istrue(a != id)
//istrue(b != ic)
//istrue(b != id)
isfalse(c != id)
isfalse(e != ie)
//istrue(ia != b)
istrue(ia != c)
istrue(ia != d)
istrue(ib != c)
istrue(ib != d)
isfalse(ic != d)
isfalse(ie != e)
// 6g used to let this go through as true.
var g uint64 = 123
var h int64 = 123
var ig interface{} = g
var ih interface{} = h
isfalse(ig == ih)
istrue(ig != ih)
// map of interface should use == on interface values,
// not memory.
// TODO: should m[c], m[d] be valid here?
var m = make(map[interface{}]int)
m[ic] = 1
m[id] = 2
if m[ic] != 2 {
println("m[ic] = ", m[ic])
panic("bad m[ic]")
if m[c] != 2 {
println("m[c] = ", m[c])
panic("bad m[c]")
}
// non-interface comparisons
{
c := make(chan int)
......@@ -103,7 +127,12 @@ func main() {
istrue(c == c2)
istrue(c1 == c)
istrue(c2 == c)
isfalse(c != c1)
isfalse(c != c2)
isfalse(c1 != c)
isfalse(c2 != c)
d := make(chan int)
isfalse(c == d)
isfalse(d == c)
......@@ -111,6 +140,13 @@ func main() {
isfalse(d == c2)
isfalse(c1 == d)
isfalse(c2 == d)
istrue(c != d)
istrue(d != c)
istrue(d != c1)
istrue(d != c2)
istrue(c1 != d)
istrue(c2 != d)
}
// named types vs not
......@@ -118,7 +154,7 @@ func main() {
var x = new(int)
var y T
var z T = x
isfalse(x == y)
istrue(x == z)
isfalse(y == z)
......@@ -126,8 +162,201 @@ func main() {
isfalse(y == x)
istrue(z == x)
isfalse(z == y)
istrue(x != y)
isfalse(x != z)
istrue(y != z)
istrue(y != x)
isfalse(z != x)
istrue(z != y)
}
// structs
{
var x = struct {
x int
y string
}{1, "hi"}
var y = struct {
x int
y string
}{2, "bye"}
var z = struct {
x int
y string
}{1, "hi"}
isfalse(x == y)
isfalse(y == x)
isfalse(y == z)
isfalse(z == y)
istrue(x == z)
istrue(z == x)
istrue(x != y)
istrue(y != x)
istrue(y != z)
istrue(z != y)
isfalse(x != z)
isfalse(z != x)
var m = make(map[struct {
x int
y string
}]int)
m[x] = 10
m[y] = 20
m[z] = 30
istrue(m[x] == 30)
istrue(m[y] == 20)
istrue(m[z] == 30)
istrue(m[x] != 10)
isfalse(m[x] != 30)
isfalse(m[y] != 20)
isfalse(m[z] != 30)
isfalse(m[x] == 10)
var m1 = make(map[struct {
x int
y string
}]struct {
x int
y string
})
m1[x] = x
m1[y] = y
m1[z] = z
istrue(m1[x] == z)
istrue(m1[y] == y)
istrue(m1[z] == z)
istrue(m1[x] == x)
isfalse(m1[x] != z)
isfalse(m1[y] != y)
isfalse(m1[z] != z)
isfalse(m1[x] != x)
var ix, iy, iz interface{} = x, y, z
isfalse(ix == iy)
isfalse(iy == ix)
isfalse(iy == iz)
isfalse(iz == iy)
istrue(ix == iz)
istrue(iz == ix)
isfalse(x == iy)
isfalse(y == ix)
isfalse(y == iz)
isfalse(z == iy)
istrue(x == iz)
istrue(z == ix)
isfalse(ix == y)
isfalse(iy == x)
isfalse(iy == z)
isfalse(iz == y)
istrue(ix == z)
istrue(iz == x)
istrue(ix != iy)
istrue(iy != ix)
istrue(iy != iz)
istrue(iz != iy)
isfalse(ix != iz)
isfalse(iz != ix)
istrue(x != iy)
istrue(y != ix)
istrue(y != iz)
istrue(z != iy)
isfalse(x != iz)
isfalse(z != ix)
istrue(ix != y)
istrue(iy != x)
istrue(iy != z)
istrue(iz != y)
isfalse(ix != z)
isfalse(iz != x)
}
// arrays
{
var x = [2]string{"1", "hi"}
var y = [2]string{"2", "bye"}
var z = [2]string{"1", "hi"}
isfalse(x == y)
isfalse(y == x)
isfalse(y == z)
isfalse(z == y)
istrue(x == z)
istrue(z == x)
istrue(x != y)
istrue(y != x)
istrue(y != z)
istrue(z != y)
isfalse(x != z)
isfalse(z != x)
var m = make(map[[2]string]int)
m[x] = 10
m[y] = 20
m[z] = 30
istrue(m[x] == 30)
istrue(m[y] == 20)
istrue(m[z] == 30)
isfalse(m[x] != 30)
isfalse(m[y] != 20)
isfalse(m[z] != 30)
var ix, iy, iz interface{} = x, y, z
isfalse(ix == iy)
isfalse(iy == ix)
isfalse(iy == iz)
isfalse(iz == iy)
istrue(ix == iz)
istrue(iz == ix)
isfalse(x == iy)
isfalse(y == ix)
isfalse(y == iz)
isfalse(z == iy)
istrue(x == iz)
istrue(z == ix)
isfalse(ix == y)
isfalse(iy == x)
isfalse(iy == z)
isfalse(iz == y)
istrue(ix == z)
istrue(iz == x)
istrue(ix != iy)
istrue(iy != ix)
istrue(iy != iz)
istrue(iz != iy)
isfalse(ix != iz)
isfalse(iz != ix)
istrue(x != iy)
istrue(y != ix)
istrue(y != iz)
istrue(z != iy)
isfalse(x != iz)
isfalse(z != ix)
istrue(ix != y)
istrue(iy != x)
istrue(iy != z)
istrue(iz != y)
isfalse(ix != z)
isfalse(iz != x)
}
shouldPanic(p1)
shouldPanic(p2)
shouldPanic(p3)
......@@ -149,14 +378,14 @@ func p2() {
func p3() {
var a []int
var ia interface{} = a
var m = make(map[interface{}] int)
var m = make(map[interface{}]int)
m[ia] = 1
}
func p4() {
var b []int
var ib interface{} = b
var m = make(map[interface{}] int)
var m = make(map[interface{}]int)
m[ib] = 1
}
......
......@@ -11,7 +11,7 @@ func use(bool) {}
type T1 *int
type T2 *int
type T3 struct{}
type T3 struct{ z []int }
var t3 T3
......@@ -54,4 +54,14 @@ func main() {
use(x == x) // ERROR "slice can only be compared to nil"
use(f == f) // ERROR "func can only be compared to nil"
use(m == m) // ERROR "map can only be compared to nil"
// Comparison with interface that cannot return true
// (would panic).
var i interface{}
use(i == x) // ERROR "invalid operation"
use(x == i) // ERROR "invalid operation"
use(i == f) // ERROR "invalid operation"
use(f == i) // ERROR "invalid operation"
use(i == m) // ERROR "invalid operation"
use(m == i) // ERROR "invalid operation"
}
......@@ -10,10 +10,10 @@ package main
func main() {
cmp(1)
var (
m map[int]int
s struct{}
s struct{ x []int }
f func()
)
noCmp(m)
......
......@@ -31,11 +31,11 @@ var (
_ map[string]v
_ map[chan int]v
_ map[*int]v
_ map[struct{}]v
_ map[[10]int]v
// invalid
_ map[struct{}]v // ERROR "invalid map key"
_ map[[]int]v // ERROR "invalid map key"
_ map[[10]int]v // ERROR "invalid map key"
_ map[func()]v // ERROR "invalid map key"
_ map[map[int]int]v // ERROR "invalid map key"
)
......@@ -60,6 +60,7 @@ func test4() {
type T struct {
a, b int
c []int
}
func test5() {
......
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