mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Phrases like "returns whether or not the image is opaque" could be describing what the function does (it always returns, regardless of the opacity) or what it returns (a boolean indicating the opacity). Even when the "or not" is missing, the phrasing is bizarre. Go with "reports whether", which is still clunky but at least makes it clear we're talking about the return value. These were edited by hand. A few were cleaned up in other ways. R=golang-dev, dsymonds CC=golang-dev https://golang.org/cl/11699043
3783 lines
68 KiB
C
3783 lines
68 KiB
C
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include "go.h"
|
|
#include "md5.h"
|
|
#include "y.tab.h"
|
|
#include "yerr.h"
|
|
|
|
typedef struct Error Error;
|
|
struct Error
|
|
{
|
|
int lineno;
|
|
int seq;
|
|
char *msg;
|
|
};
|
|
static Error *err;
|
|
static int nerr;
|
|
static int merr;
|
|
|
|
void
|
|
errorexit(void)
|
|
{
|
|
flusherrors();
|
|
if(outfile)
|
|
remove(outfile);
|
|
exits("error");
|
|
}
|
|
|
|
extern int yychar;
|
|
int
|
|
parserline(void)
|
|
{
|
|
if(yychar != 0 && yychar != -2) // parser has one symbol lookahead
|
|
return prevlineno;
|
|
return lineno;
|
|
}
|
|
|
|
static void
|
|
adderr(int line, char *fmt, va_list arg)
|
|
{
|
|
Fmt f;
|
|
Error *p;
|
|
|
|
fmtstrinit(&f);
|
|
fmtprint(&f, "%L: ", line);
|
|
fmtvprint(&f, fmt, arg);
|
|
fmtprint(&f, "\n");
|
|
|
|
if(nerr >= merr) {
|
|
if(merr == 0)
|
|
merr = 16;
|
|
else
|
|
merr *= 2;
|
|
p = realloc(err, merr*sizeof err[0]);
|
|
if(p == nil) {
|
|
merr = nerr;
|
|
flusherrors();
|
|
print("out of memory\n");
|
|
errorexit();
|
|
}
|
|
err = p;
|
|
}
|
|
err[nerr].seq = nerr;
|
|
err[nerr].lineno = line;
|
|
err[nerr].msg = fmtstrflush(&f);
|
|
nerr++;
|
|
}
|
|
|
|
static int
|
|
errcmp(const void *va, const void *vb)
|
|
{
|
|
Error *a, *b;
|
|
|
|
a = (Error*)va;
|
|
b = (Error*)vb;
|
|
if(a->lineno != b->lineno)
|
|
return a->lineno - b->lineno;
|
|
if(a->seq != b->seq)
|
|
return a->seq - b->seq;
|
|
return strcmp(a->msg, b->msg);
|
|
}
|
|
|
|
void
|
|
flusherrors(void)
|
|
{
|
|
int i;
|
|
|
|
if(nerr == 0)
|
|
return;
|
|
qsort(err, nerr, sizeof err[0], errcmp);
|
|
for(i=0; i<nerr; i++)
|
|
if(i==0 || strcmp(err[i].msg, err[i-1].msg) != 0)
|
|
print("%s", err[i].msg);
|
|
nerr = 0;
|
|
}
|
|
|
|
static void
|
|
hcrash(void)
|
|
{
|
|
if(debug['h']) {
|
|
flusherrors();
|
|
if(outfile)
|
|
remove(outfile);
|
|
*(volatile int*)0 = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
yyerrorl(int line, char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
adderr(line, fmt, arg);
|
|
va_end(arg);
|
|
|
|
hcrash();
|
|
nerrors++;
|
|
if(nsavederrors+nerrors >= 10 && !debug['e']) {
|
|
flusherrors();
|
|
print("%L: too many errors\n", line);
|
|
errorexit();
|
|
}
|
|
}
|
|
|
|
extern int yystate, yychar;
|
|
|
|
void
|
|
yyerror(char *fmt, ...)
|
|
{
|
|
int i;
|
|
static int lastsyntax;
|
|
va_list arg;
|
|
char buf[512], *p;
|
|
|
|
if(strncmp(fmt, "syntax error", 12) == 0) {
|
|
nsyntaxerrors++;
|
|
|
|
if(debug['x'])
|
|
print("yyerror: yystate=%d yychar=%d\n", yystate, yychar);
|
|
|
|
// An unexpected EOF caused a syntax error. Use the previous
|
|
// line number since getc generated a fake newline character.
|
|
if(curio.eofnl)
|
|
lexlineno = prevlineno;
|
|
|
|
// only one syntax error per line
|
|
if(lastsyntax == lexlineno)
|
|
return;
|
|
lastsyntax = lexlineno;
|
|
|
|
if(strstr(fmt, "{ or {") || strstr(fmt, " or ?") || strstr(fmt, " or @")) {
|
|
// The grammar has { and LBRACE but both show up as {.
|
|
// Rewrite syntax error referring to "{ or {" to say just "{".
|
|
strecpy(buf, buf+sizeof buf, fmt);
|
|
p = strstr(buf, "{ or {");
|
|
if(p)
|
|
memmove(p+1, p+6, strlen(p+6)+1);
|
|
|
|
// The grammar has ? and @ but only for reading imports.
|
|
// Silence them in ordinary errors.
|
|
p = strstr(buf, " or ?");
|
|
if(p)
|
|
memmove(p, p+5, strlen(p+5)+1);
|
|
p = strstr(buf, " or @");
|
|
if(p)
|
|
memmove(p, p+5, strlen(p+5)+1);
|
|
fmt = buf;
|
|
}
|
|
|
|
// look for parse state-specific errors in list (see go.errors).
|
|
for(i=0; i<nelem(yymsg); i++) {
|
|
if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
|
|
yyerrorl(lexlineno, "syntax error: %s", yymsg[i].msg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// plain "syntax error" gets "near foo" added
|
|
if(strcmp(fmt, "syntax error") == 0) {
|
|
yyerrorl(lexlineno, "syntax error near %s", lexbuf);
|
|
return;
|
|
}
|
|
|
|
// if bison says "syntax error, more info"; print "syntax error: more info".
|
|
if(fmt[12] == ',') {
|
|
yyerrorl(lexlineno, "syntax error:%s", fmt+13);
|
|
return;
|
|
}
|
|
|
|
yyerrorl(lexlineno, "%s", fmt);
|
|
return;
|
|
}
|
|
|
|
va_start(arg, fmt);
|
|
adderr(parserline(), fmt, arg);
|
|
va_end(arg);
|
|
|
|
hcrash();
|
|
nerrors++;
|
|
if(nsavederrors+nerrors >= 10 && !debug['e']) {
|
|
flusherrors();
|
|
print("%L: too many errors\n", parserline());
|
|
errorexit();
|
|
}
|
|
}
|
|
|
|
void
|
|
warn(char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
adderr(parserline(), fmt, arg);
|
|
va_end(arg);
|
|
|
|
hcrash();
|
|
}
|
|
|
|
void
|
|
warnl(int line, char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
adderr(line, fmt, arg);
|
|
va_end(arg);
|
|
if(debug['m'])
|
|
flusherrors();
|
|
}
|
|
|
|
void
|
|
fatal(char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
flusherrors();
|
|
|
|
print("%L: internal compiler error: ", lineno);
|
|
va_start(arg, fmt);
|
|
vfprint(1, fmt, arg);
|
|
va_end(arg);
|
|
print("\n");
|
|
|
|
// If this is a released compiler version, ask for a bug report.
|
|
if(strncmp(getgoversion(), "release", 7) == 0) {
|
|
print("\n");
|
|
print("Please file a bug report including a short program that triggers the error.\n");
|
|
print("http://code.google.com/p/go/issues/entry?template=compilerbug\n");
|
|
}
|
|
hcrash();
|
|
errorexit();
|
|
}
|
|
|
|
void
|
|
linehist(char *file, int32 off, int relative)
|
|
{
|
|
Hist *h;
|
|
char *cp;
|
|
|
|
if(debug['i']) {
|
|
if(file != nil) {
|
|
if(off < 0)
|
|
print("pragma %s", file);
|
|
else
|
|
if(off > 0)
|
|
print("line %s", file);
|
|
else
|
|
print("import %s", file);
|
|
} else
|
|
print("end of import");
|
|
print(" at line %L\n", lexlineno);
|
|
}
|
|
|
|
if(off < 0 && file[0] != '/' && !relative) {
|
|
cp = mal(strlen(file) + strlen(pathname) + 2);
|
|
sprint(cp, "%s/%s", pathname, file);
|
|
file = cp;
|
|
}
|
|
|
|
h = mal(sizeof(Hist));
|
|
h->name = file;
|
|
h->line = lexlineno;
|
|
h->offset = off;
|
|
h->link = H;
|
|
if(ehist == H) {
|
|
hist = h;
|
|
ehist = h;
|
|
return;
|
|
}
|
|
ehist->link = h;
|
|
ehist = h;
|
|
}
|
|
|
|
int32
|
|
setlineno(Node *n)
|
|
{
|
|
int32 lno;
|
|
|
|
lno = lineno;
|
|
if(n != N)
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OTYPE:
|
|
case OPACK:
|
|
case OLITERAL:
|
|
break;
|
|
default:
|
|
lineno = n->lineno;
|
|
if(lineno == 0) {
|
|
if(debug['K'])
|
|
warn("setlineno: line 0");
|
|
lineno = lno;
|
|
}
|
|
}
|
|
return lno;
|
|
}
|
|
|
|
uint32
|
|
stringhash(char *p)
|
|
{
|
|
int32 h;
|
|
int c;
|
|
|
|
h = 0;
|
|
for(;;) {
|
|
c = *p++;
|
|
if(c == 0)
|
|
break;
|
|
h = h*PRIME1 + c;
|
|
}
|
|
|
|
if(h < 0) {
|
|
h = -h;
|
|
if(h < 0)
|
|
h = 0;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
Sym*
|
|
lookup(char *name)
|
|
{
|
|
return pkglookup(name, localpkg);
|
|
}
|
|
|
|
Sym*
|
|
pkglookup(char *name, Pkg *pkg)
|
|
{
|
|
Sym *s;
|
|
uint32 h;
|
|
int c;
|
|
|
|
h = stringhash(name) % NHASH;
|
|
c = name[0];
|
|
for(s = hash[h]; s != S; s = s->link) {
|
|
if(s->name[0] != c || s->pkg != pkg)
|
|
continue;
|
|
if(strcmp(s->name, name) == 0)
|
|
return s;
|
|
}
|
|
|
|
s = mal(sizeof(*s));
|
|
s->name = mal(strlen(name)+1);
|
|
strcpy(s->name, name);
|
|
|
|
s->pkg = pkg;
|
|
|
|
s->link = hash[h];
|
|
hash[h] = s;
|
|
s->lexical = LNAME;
|
|
|
|
return s;
|
|
}
|
|
|
|
Sym*
|
|
restrictlookup(char *name, Pkg *pkg)
|
|
{
|
|
if(!exportname(name) && pkg != localpkg)
|
|
yyerror("cannot refer to unexported name %s.%s", pkg->name, name);
|
|
return pkglookup(name, pkg);
|
|
}
|
|
|
|
|
|
// find all the exported symbols in package opkg
|
|
// and make them available in the current package
|
|
void
|
|
importdot(Pkg *opkg, Node *pack)
|
|
{
|
|
Sym *s, *s1;
|
|
uint32 h;
|
|
int n;
|
|
char *pkgerror;
|
|
|
|
n = 0;
|
|
for(h=0; h<NHASH; h++) {
|
|
for(s = hash[h]; s != S; s = s->link) {
|
|
if(s->pkg != opkg)
|
|
continue;
|
|
if(s->def == N)
|
|
continue;
|
|
if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot
|
|
continue;
|
|
s1 = lookup(s->name);
|
|
if(s1->def != N) {
|
|
pkgerror = smprint("during import \"%Z\"", opkg->path);
|
|
redeclare(s1, pkgerror);
|
|
continue;
|
|
}
|
|
s1->def = s->def;
|
|
s1->block = s->block;
|
|
s1->def->pack = pack;
|
|
s1->origpkg = opkg;
|
|
n++;
|
|
}
|
|
}
|
|
if(n == 0) {
|
|
// can't possibly be used - there were no symbols
|
|
yyerrorl(pack->lineno, "imported and not used: \"%Z\"", opkg->path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gethunk(void)
|
|
{
|
|
char *h;
|
|
int32 nh;
|
|
|
|
nh = NHUNK;
|
|
if(thunk >= 10L*NHUNK)
|
|
nh = 10L*NHUNK;
|
|
h = (char*)malloc(nh);
|
|
if(h == nil) {
|
|
flusherrors();
|
|
yyerror("out of memory");
|
|
errorexit();
|
|
}
|
|
hunk = h;
|
|
nhunk = nh;
|
|
thunk += nh;
|
|
}
|
|
|
|
void*
|
|
mal(int32 n)
|
|
{
|
|
void *p;
|
|
|
|
if(n >= NHUNK) {
|
|
p = malloc(n);
|
|
if(p == nil) {
|
|
flusherrors();
|
|
yyerror("out of memory");
|
|
errorexit();
|
|
}
|
|
memset(p, 0, n);
|
|
return p;
|
|
}
|
|
|
|
while((uintptr)hunk & MAXALIGN) {
|
|
hunk++;
|
|
nhunk--;
|
|
}
|
|
if(nhunk < n)
|
|
gethunk();
|
|
|
|
p = hunk;
|
|
nhunk -= n;
|
|
hunk += n;
|
|
memset(p, 0, n);
|
|
return p;
|
|
}
|
|
|
|
void*
|
|
remal(void *p, int32 on, int32 n)
|
|
{
|
|
void *q;
|
|
|
|
q = (uchar*)p + on;
|
|
if(q != hunk || nhunk < n) {
|
|
if(on+n >= NHUNK) {
|
|
q = mal(on+n);
|
|
memmove(q, p, on);
|
|
return q;
|
|
}
|
|
if(nhunk < on+n)
|
|
gethunk();
|
|
memmove(hunk, p, on);
|
|
p = hunk;
|
|
hunk += on;
|
|
nhunk -= on;
|
|
}
|
|
hunk += n;
|
|
nhunk -= n;
|
|
return p;
|
|
}
|
|
|
|
Node*
|
|
nod(int op, Node *nleft, Node *nright)
|
|
{
|
|
Node *n;
|
|
|
|
n = mal(sizeof(*n));
|
|
n->op = op;
|
|
n->left = nleft;
|
|
n->right = nright;
|
|
n->lineno = parserline();
|
|
n->xoffset = BADWIDTH;
|
|
n->orig = n;
|
|
n->curfn = curfn;
|
|
return n;
|
|
}
|
|
|
|
void
|
|
saveorignode(Node *n)
|
|
{
|
|
Node *norig;
|
|
|
|
if(n->orig != N)
|
|
return;
|
|
norig = nod(n->op, N, N);
|
|
*norig = *n;
|
|
n->orig = norig;
|
|
}
|
|
|
|
// ispaddedfield reports whether the given field
|
|
// is followed by padding. For the case where t is
|
|
// the last field, total gives the size of the enclosing struct.
|
|
static int
|
|
ispaddedfield(Type *t, vlong total)
|
|
{
|
|
if(t->etype != TFIELD)
|
|
fatal("ispaddedfield called non-field %T", t);
|
|
if(t->down == T)
|
|
return t->width + t->type->width != total;
|
|
return t->width + t->type->width != t->down->width;
|
|
}
|
|
|
|
int
|
|
algtype1(Type *t, Type **bad)
|
|
{
|
|
int a, ret;
|
|
Type *t1;
|
|
|
|
if(bad)
|
|
*bad = T;
|
|
|
|
switch(t->etype) {
|
|
case TANY:
|
|
case TFORW:
|
|
// will be defined later.
|
|
*bad = t;
|
|
return -1;
|
|
|
|
case TINT8:
|
|
case TUINT8:
|
|
case TINT16:
|
|
case TUINT16:
|
|
case TINT32:
|
|
case TUINT32:
|
|
case TINT64:
|
|
case TUINT64:
|
|
case TINT:
|
|
case TUINT:
|
|
case TUINTPTR:
|
|
case TBOOL:
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TCHAN:
|
|
case TUNSAFEPTR:
|
|
return AMEM;
|
|
|
|
case TFUNC:
|
|
case TMAP:
|
|
if(bad)
|
|
*bad = t;
|
|
return ANOEQ;
|
|
|
|
case TFLOAT32:
|
|
return AFLOAT32;
|
|
|
|
case TFLOAT64:
|
|
return AFLOAT64;
|
|
|
|
case TCOMPLEX64:
|
|
return ACPLX64;
|
|
|
|
case TCOMPLEX128:
|
|
return ACPLX128;
|
|
|
|
case TSTRING:
|
|
return ASTRING;
|
|
|
|
case TINTER:
|
|
if(isnilinter(t))
|
|
return ANILINTER;
|
|
return AINTER;
|
|
|
|
case TARRAY:
|
|
if(isslice(t)) {
|
|
if(bad)
|
|
*bad = t;
|
|
return ANOEQ;
|
|
}
|
|
if(t->bound == 0)
|
|
return AMEM;
|
|
a = algtype1(t->type, bad);
|
|
if(a == ANOEQ || a == AMEM) {
|
|
if(a == ANOEQ && bad)
|
|
*bad = t;
|
|
return a;
|
|
}
|
|
return -1; // needs special compare
|
|
|
|
case TSTRUCT:
|
|
if(t->type != T && t->type->down == T && !isblanksym(t->type->sym)) {
|
|
// One-field struct is same as that one field alone.
|
|
return algtype1(t->type->type, bad);
|
|
}
|
|
ret = AMEM;
|
|
for(t1=t->type; t1!=T; t1=t1->down) {
|
|
// All fields must be comparable.
|
|
a = algtype1(t1->type, bad);
|
|
if(a == ANOEQ)
|
|
return ANOEQ;
|
|
|
|
// Blank fields, padded fields, fields with non-memory
|
|
// equality need special compare.
|
|
if(a != AMEM || isblanksym(t1->sym) || ispaddedfield(t1, t->width)) {
|
|
ret = -1;
|
|
continue;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
fatal("algtype1: unexpected type %T", t);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
algtype(Type *t)
|
|
{
|
|
int a;
|
|
|
|
a = algtype1(t, nil);
|
|
if(a == AMEM || a == ANOEQ) {
|
|
if(isslice(t))
|
|
return ASLICE;
|
|
switch(t->width) {
|
|
case 0:
|
|
return a + AMEM0 - AMEM;
|
|
case 1:
|
|
return a + AMEM8 - AMEM;
|
|
case 2:
|
|
return a + AMEM16 - AMEM;
|
|
case 4:
|
|
return a + AMEM32 - AMEM;
|
|
case 8:
|
|
return a + AMEM64 - AMEM;
|
|
case 16:
|
|
return a + AMEM128 - AMEM;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
Type*
|
|
maptype(Type *key, Type *val)
|
|
{
|
|
Type *t;
|
|
Type *bad;
|
|
int atype;
|
|
|
|
if(key != nil) {
|
|
atype = algtype1(key, &bad);
|
|
switch(bad == T ? key->etype : bad->etype) {
|
|
default:
|
|
if(atype == ANOEQ)
|
|
yyerror("invalid map key type %T", key);
|
|
break;
|
|
case TANY:
|
|
// will be resolved later.
|
|
break;
|
|
case TFORW:
|
|
// map[key] used during definition of key.
|
|
// postpone check until key is fully defined.
|
|
// if there are multiple uses of map[key]
|
|
// before key is fully defined, the error
|
|
// will only be printed for the first one.
|
|
// good enough.
|
|
if(key->maplineno == 0)
|
|
key->maplineno = lineno;
|
|
break;
|
|
}
|
|
}
|
|
t = typ(TMAP);
|
|
t->down = key;
|
|
t->type = val;
|
|
return t;
|
|
}
|
|
|
|
Type*
|
|
typ(int et)
|
|
{
|
|
Type *t;
|
|
|
|
t = mal(sizeof(*t));
|
|
t->etype = et;
|
|
t->width = BADWIDTH;
|
|
t->lineno = lineno;
|
|
t->orig = t;
|
|
return t;
|
|
}
|
|
|
|
static int
|
|
methcmp(const void *va, const void *vb)
|
|
{
|
|
Type *a, *b;
|
|
int i;
|
|
|
|
a = *(Type**)va;
|
|
b = *(Type**)vb;
|
|
if(a->sym == S && b->sym == S)
|
|
return 0;
|
|
if(a->sym == S)
|
|
return -1;
|
|
if(b->sym == S)
|
|
return 1;
|
|
i = strcmp(a->sym->name, b->sym->name);
|
|
if(i != 0)
|
|
return i;
|
|
if(!exportname(a->sym->name)) {
|
|
i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
|
|
if(i != 0)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Type*
|
|
sortinter(Type *t)
|
|
{
|
|
Type *f;
|
|
int i;
|
|
Type **a;
|
|
|
|
if(t->type == nil || t->type->down == nil)
|
|
return t;
|
|
|
|
i=0;
|
|
for(f=t->type; f; f=f->down)
|
|
i++;
|
|
a = mal(i*sizeof f);
|
|
i = 0;
|
|
for(f=t->type; f; f=f->down)
|
|
a[i++] = f;
|
|
qsort(a, i, sizeof a[0], methcmp);
|
|
while(i-- > 0) {
|
|
a[i]->down = f;
|
|
f = a[i];
|
|
}
|
|
t->type = f;
|
|
return t;
|
|
}
|
|
|
|
Node*
|
|
nodintconst(int64 v)
|
|
{
|
|
Node *c;
|
|
|
|
c = nod(OLITERAL, N, N);
|
|
c->addable = 1;
|
|
c->val.u.xval = mal(sizeof(*c->val.u.xval));
|
|
mpmovecfix(c->val.u.xval, v);
|
|
c->val.ctype = CTINT;
|
|
c->type = types[TIDEAL];
|
|
ullmancalc(c);
|
|
return c;
|
|
}
|
|
|
|
Node*
|
|
nodfltconst(Mpflt* v)
|
|
{
|
|
Node *c;
|
|
|
|
c = nod(OLITERAL, N, N);
|
|
c->addable = 1;
|
|
c->val.u.fval = mal(sizeof(*c->val.u.fval));
|
|
mpmovefltflt(c->val.u.fval, v);
|
|
c->val.ctype = CTFLT;
|
|
c->type = types[TIDEAL];
|
|
ullmancalc(c);
|
|
return c;
|
|
}
|
|
|
|
void
|
|
nodconst(Node *n, Type *t, int64 v)
|
|
{
|
|
memset(n, 0, sizeof(*n));
|
|
n->op = OLITERAL;
|
|
n->addable = 1;
|
|
ullmancalc(n);
|
|
n->val.u.xval = mal(sizeof(*n->val.u.xval));
|
|
mpmovecfix(n->val.u.xval, v);
|
|
n->val.ctype = CTINT;
|
|
n->type = t;
|
|
|
|
if(isfloat[t->etype])
|
|
fatal("nodconst: bad type %T", t);
|
|
}
|
|
|
|
Node*
|
|
nodnil(void)
|
|
{
|
|
Node *c;
|
|
|
|
c = nodintconst(0);
|
|
c->val.ctype = CTNIL;
|
|
c->type = types[TNIL];
|
|
return c;
|
|
}
|
|
|
|
Node*
|
|
nodbool(int b)
|
|
{
|
|
Node *c;
|
|
|
|
c = nodintconst(0);
|
|
c->val.ctype = CTBOOL;
|
|
c->val.u.bval = b;
|
|
c->type = idealbool;
|
|
return c;
|
|
}
|
|
|
|
Type*
|
|
aindex(Node *b, Type *t)
|
|
{
|
|
Type *r;
|
|
int64 bound;
|
|
|
|
bound = -1; // open bound
|
|
typecheck(&b, Erv);
|
|
if(b != nil) {
|
|
switch(consttype(b)) {
|
|
default:
|
|
yyerror("array bound must be an integer expression");
|
|
break;
|
|
case CTINT:
|
|
case CTRUNE:
|
|
bound = mpgetfix(b->val.u.xval);
|
|
if(bound < 0)
|
|
yyerror("array bound must be non negative");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// fixed array
|
|
r = typ(TARRAY);
|
|
r->type = t;
|
|
r->bound = bound;
|
|
return r;
|
|
}
|
|
|
|
Node*
|
|
treecopy(Node *n)
|
|
{
|
|
Node *m;
|
|
|
|
if(n == N)
|
|
return N;
|
|
|
|
switch(n->op) {
|
|
default:
|
|
m = nod(OXXX, N, N);
|
|
*m = *n;
|
|
m->orig = m;
|
|
m->left = treecopy(n->left);
|
|
m->right = treecopy(n->right);
|
|
m->list = listtreecopy(n->list);
|
|
if(m->defn)
|
|
abort();
|
|
break;
|
|
|
|
case ONONAME:
|
|
if(n->sym == lookup("iota")) {
|
|
// Not sure yet whether this is the real iota,
|
|
// but make a copy of the Node* just in case,
|
|
// so that all the copies of this const definition
|
|
// don't have the same iota value.
|
|
m = nod(OXXX, N, N);
|
|
*m = *n;
|
|
m->iota = iota;
|
|
break;
|
|
}
|
|
// fall through
|
|
case ONAME:
|
|
case OLITERAL:
|
|
case OTYPE:
|
|
m = n;
|
|
break;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
int
|
|
isnil(Node *n)
|
|
{
|
|
if(n == N)
|
|
return 0;
|
|
if(n->op != OLITERAL)
|
|
return 0;
|
|
if(n->val.ctype != CTNIL)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
isptrto(Type *t, int et)
|
|
{
|
|
if(t == T)
|
|
return 0;
|
|
if(!isptr[t->etype])
|
|
return 0;
|
|
t = t->type;
|
|
if(t == T)
|
|
return 0;
|
|
if(t->etype != et)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
istype(Type *t, int et)
|
|
{
|
|
return t != T && t->etype == et;
|
|
}
|
|
|
|
int
|
|
isfixedarray(Type *t)
|
|
{
|
|
return t != T && t->etype == TARRAY && t->bound >= 0;
|
|
}
|
|
|
|
int
|
|
isslice(Type *t)
|
|
{
|
|
return t != T && t->etype == TARRAY && t->bound < 0;
|
|
}
|
|
|
|
int
|
|
isblank(Node *n)
|
|
{
|
|
if(n == N)
|
|
return 0;
|
|
return isblanksym(n->sym);
|
|
}
|
|
|
|
int
|
|
isblanksym(Sym *s)
|
|
{
|
|
char *p;
|
|
|
|
if(s == S)
|
|
return 0;
|
|
p = s->name;
|
|
if(p == nil)
|
|
return 0;
|
|
return p[0] == '_' && p[1] == '\0';
|
|
}
|
|
|
|
int
|
|
isinter(Type *t)
|
|
{
|
|
return t != T && t->etype == TINTER;
|
|
}
|
|
|
|
int
|
|
isnilinter(Type *t)
|
|
{
|
|
if(!isinter(t))
|
|
return 0;
|
|
if(t->type != T)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
isideal(Type *t)
|
|
{
|
|
if(t == T)
|
|
return 0;
|
|
if(t == idealstring || t == idealbool)
|
|
return 1;
|
|
switch(t->etype) {
|
|
case TNIL:
|
|
case TIDEAL:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* given receiver of type t (t == r or t == *r)
|
|
* return type to hang methods off (r).
|
|
*/
|
|
Type*
|
|
methtype(Type *t, int mustname)
|
|
{
|
|
if(t == T)
|
|
return T;
|
|
|
|
// strip away pointer if it's there
|
|
if(isptr[t->etype]) {
|
|
if(t->sym != S)
|
|
return T;
|
|
t = t->type;
|
|
if(t == T)
|
|
return T;
|
|
}
|
|
|
|
// need a type name
|
|
if(t->sym == S && (mustname || t->etype != TSTRUCT))
|
|
return T;
|
|
|
|
// check types
|
|
if(!issimple[t->etype])
|
|
switch(t->etype) {
|
|
default:
|
|
return T;
|
|
case TSTRUCT:
|
|
case TARRAY:
|
|
case TMAP:
|
|
case TCHAN:
|
|
case TSTRING:
|
|
case TFUNC:
|
|
break;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
int
|
|
cplxsubtype(int et)
|
|
{
|
|
switch(et) {
|
|
case TCOMPLEX64:
|
|
return TFLOAT32;
|
|
case TCOMPLEX128:
|
|
return TFLOAT64;
|
|
}
|
|
fatal("cplxsubtype: %E\n", et);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
eqnote(Strlit *a, Strlit *b)
|
|
{
|
|
if(a == b)
|
|
return 1;
|
|
if(a == nil || b == nil)
|
|
return 0;
|
|
if(a->len != b->len)
|
|
return 0;
|
|
return memcmp(a->s, b->s, a->len) == 0;
|
|
}
|
|
|
|
typedef struct TypePairList TypePairList;
|
|
struct TypePairList
|
|
{
|
|
Type *t1;
|
|
Type *t2;
|
|
TypePairList *next;
|
|
};
|
|
|
|
static int
|
|
onlist(TypePairList *l, Type *t1, Type *t2)
|
|
{
|
|
for(; l; l=l->next)
|
|
if((l->t1 == t1 && l->t2 == t2) || (l->t1 == t2 && l->t2 == t1))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int eqtype1(Type*, Type*, TypePairList*);
|
|
|
|
// Return 1 if t1 and t2 are identical, following the spec rules.
|
|
//
|
|
// Any cyclic type must go through a named type, and if one is
|
|
// named, it is only identical to the other if they are the same
|
|
// pointer (t1 == t2), so there's no chance of chasing cycles
|
|
// ad infinitum, so no need for a depth counter.
|
|
int
|
|
eqtype(Type *t1, Type *t2)
|
|
{
|
|
return eqtype1(t1, t2, nil);
|
|
}
|
|
|
|
static int
|
|
eqtype1(Type *t1, Type *t2, TypePairList *assumed_equal)
|
|
{
|
|
TypePairList l;
|
|
|
|
if(t1 == t2)
|
|
return 1;
|
|
if(t1 == T || t2 == T || t1->etype != t2->etype)
|
|
return 0;
|
|
if(t1->sym || t2->sym) {
|
|
// Special case: we keep byte and uint8 separate
|
|
// for error messages. Treat them as equal.
|
|
switch(t1->etype) {
|
|
case TUINT8:
|
|
if((t1 == types[TUINT8] || t1 == bytetype) && (t2 == types[TUINT8] || t2 == bytetype))
|
|
return 1;
|
|
break;
|
|
case TINT:
|
|
case TINT32:
|
|
if((t1 == types[runetype->etype] || t1 == runetype) && (t2 == types[runetype->etype] || t2 == runetype))
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if(onlist(assumed_equal, t1, t2))
|
|
return 1;
|
|
l.next = assumed_equal;
|
|
l.t1 = t1;
|
|
l.t2 = t2;
|
|
|
|
switch(t1->etype) {
|
|
case TINTER:
|
|
case TSTRUCT:
|
|
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
|
|
if(t1->etype != TFIELD || t2->etype != TFIELD)
|
|
fatal("struct/interface missing field: %T %T", t1, t2);
|
|
if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype1(t1->type, t2->type, &l) || !eqnote(t1->note, t2->note))
|
|
goto no;
|
|
}
|
|
if(t1 == T && t2 == T)
|
|
goto yes;
|
|
goto no;
|
|
|
|
case TFUNC:
|
|
// Loop over structs: receiver, in, out.
|
|
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
|
|
Type *ta, *tb;
|
|
|
|
if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
|
|
fatal("func missing struct: %T %T", t1, t2);
|
|
|
|
// Loop over fields in structs, ignoring argument names.
|
|
for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
|
|
if(ta->etype != TFIELD || tb->etype != TFIELD)
|
|
fatal("func struct missing field: %T %T", ta, tb);
|
|
if(ta->isddd != tb->isddd || !eqtype1(ta->type, tb->type, &l))
|
|
goto no;
|
|
}
|
|
if(ta != T || tb != T)
|
|
goto no;
|
|
}
|
|
if(t1 == T && t2 == T)
|
|
goto yes;
|
|
goto no;
|
|
|
|
case TARRAY:
|
|
if(t1->bound != t2->bound)
|
|
goto no;
|
|
break;
|
|
|
|
case TCHAN:
|
|
if(t1->chan != t2->chan)
|
|
goto no;
|
|
break;
|
|
}
|
|
|
|
if(eqtype1(t1->down, t2->down, &l) && eqtype1(t1->type, t2->type, &l))
|
|
goto yes;
|
|
goto no;
|
|
|
|
yes:
|
|
return 1;
|
|
|
|
no:
|
|
return 0;
|
|
}
|
|
|
|
// Are t1 and t2 equal struct types when field names are ignored?
|
|
// For deciding whether the result struct from g can be copied
|
|
// directly when compiling f(g()).
|
|
int
|
|
eqtypenoname(Type *t1, Type *t2)
|
|
{
|
|
if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT)
|
|
return 0;
|
|
|
|
t1 = t1->type;
|
|
t2 = t2->type;
|
|
for(;;) {
|
|
if(!eqtype(t1, t2))
|
|
return 0;
|
|
if(t1 == T)
|
|
return 1;
|
|
t1 = t1->down;
|
|
t2 = t2->down;
|
|
}
|
|
}
|
|
|
|
// Is type src assignment compatible to type dst?
|
|
// If so, return op code to use in conversion.
|
|
// If not, return 0.
|
|
int
|
|
assignop(Type *src, Type *dst, char **why)
|
|
{
|
|
Type *missing, *have;
|
|
int ptr;
|
|
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
// TODO(rsc,lvd): This behaves poorly in the presence of inlining.
|
|
// https://code.google.com/p/go/issues/detail?id=2795
|
|
if(safemode && importpkg == nil && src != T && src->etype == TUNSAFEPTR) {
|
|
yyerror("cannot use unsafe.Pointer");
|
|
errorexit();
|
|
}
|
|
|
|
if(src == dst)
|
|
return OCONVNOP;
|
|
if(src == T || dst == T || src->etype == TFORW || dst->etype == TFORW || src->orig == T || dst->orig == T)
|
|
return 0;
|
|
|
|
// 1. src type is identical to dst.
|
|
if(eqtype(src, dst))
|
|
return OCONVNOP;
|
|
|
|
// 2. src and dst have identical underlying types
|
|
// and either src or dst is not a named type or
|
|
// both are interface types.
|
|
if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER))
|
|
return OCONVNOP;
|
|
|
|
// 3. dst is an interface type and src implements dst.
|
|
if(dst->etype == TINTER && src->etype != TNIL) {
|
|
if(implements(src, dst, &missing, &have, &ptr))
|
|
return OCONVIFACE;
|
|
|
|
// we'll have complained about this method anyway, supress spurious messages.
|
|
if(have && have->sym == missing->sym && (have->type->broke || missing->type->broke))
|
|
return OCONVIFACE;
|
|
|
|
if(why != nil) {
|
|
if(isptrto(src, TINTER))
|
|
*why = smprint(":\n\t%T is pointer to interface, not interface", src);
|
|
else if(have && have->sym == missing->sym)
|
|
*why = smprint(":\n\t%T does not implement %T (wrong type for %S method)\n"
|
|
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
|
|
have->sym, have->type, missing->sym, missing->type);
|
|
else if(ptr)
|
|
*why = smprint(":\n\t%T does not implement %T (%S method has pointer receiver)",
|
|
src, dst, missing->sym);
|
|
else if(have)
|
|
*why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
|
|
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
|
|
have->sym, have->type, missing->sym, missing->type);
|
|
else
|
|
*why = smprint(":\n\t%T does not implement %T (missing %S method)",
|
|
src, dst, missing->sym);
|
|
}
|
|
return 0;
|
|
}
|
|
if(isptrto(dst, TINTER)) {
|
|
if(why != nil)
|
|
*why = smprint(":\n\t%T is pointer to interface, not interface", dst);
|
|
return 0;
|
|
}
|
|
if(src->etype == TINTER && dst->etype != TBLANK) {
|
|
if(why != nil && implements(dst, src, &missing, &have, &ptr))
|
|
*why = ": need type assertion";
|
|
return 0;
|
|
}
|
|
|
|
// 4. src is a bidirectional channel value, dst is a channel type,
|
|
// src and dst have identical element types, and
|
|
// either src or dst is not a named type.
|
|
if(src->etype == TCHAN && src->chan == Cboth && dst->etype == TCHAN)
|
|
if(eqtype(src->type, dst->type) && (src->sym == S || dst->sym == S))
|
|
return OCONVNOP;
|
|
|
|
// 5. src is the predeclared identifier nil and dst is a nillable type.
|
|
if(src->etype == TNIL) {
|
|
switch(dst->etype) {
|
|
case TARRAY:
|
|
if(dst->bound != -100) // not slice
|
|
break;
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TFUNC:
|
|
case TMAP:
|
|
case TCHAN:
|
|
case TINTER:
|
|
return OCONVNOP;
|
|
}
|
|
}
|
|
|
|
// 6. rule about untyped constants - already converted by defaultlit.
|
|
|
|
// 7. Any typed value can be assigned to the blank identifier.
|
|
if(dst->etype == TBLANK)
|
|
return OCONVNOP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Can we convert a value of type src to a value of type dst?
|
|
// If so, return op code to use in conversion (maybe OCONVNOP).
|
|
// If not, return 0.
|
|
int
|
|
convertop(Type *src, Type *dst, char **why)
|
|
{
|
|
int op;
|
|
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
if(src == dst)
|
|
return OCONVNOP;
|
|
if(src == T || dst == T)
|
|
return 0;
|
|
|
|
// 1. src can be assigned to dst.
|
|
if((op = assignop(src, dst, why)) != 0)
|
|
return op;
|
|
|
|
// The rules for interfaces are no different in conversions
|
|
// than assignments. If interfaces are involved, stop now
|
|
// with the good message from assignop.
|
|
// Otherwise clear the error.
|
|
if(src->etype == TINTER || dst->etype == TINTER)
|
|
return 0;
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
// 2. src and dst have identical underlying types.
|
|
if(eqtype(src->orig, dst->orig))
|
|
return OCONVNOP;
|
|
|
|
// 3. src and dst are unnamed pointer types
|
|
// and their base types have identical underlying types.
|
|
if(isptr[src->etype] && isptr[dst->etype] && src->sym == S && dst->sym == S)
|
|
if(eqtype(src->type->orig, dst->type->orig))
|
|
return OCONVNOP;
|
|
|
|
// 4. src and dst are both integer or floating point types.
|
|
if((isint[src->etype] || isfloat[src->etype]) && (isint[dst->etype] || isfloat[dst->etype])) {
|
|
if(simtype[src->etype] == simtype[dst->etype])
|
|
return OCONVNOP;
|
|
return OCONV;
|
|
}
|
|
|
|
// 5. src and dst are both complex types.
|
|
if(iscomplex[src->etype] && iscomplex[dst->etype]) {
|
|
if(simtype[src->etype] == simtype[dst->etype])
|
|
return OCONVNOP;
|
|
return OCONV;
|
|
}
|
|
|
|
// 6. src is an integer or has type []byte or []rune
|
|
// and dst is a string type.
|
|
if(isint[src->etype] && dst->etype == TSTRING)
|
|
return ORUNESTR;
|
|
|
|
if(isslice(src) && dst->etype == TSTRING) {
|
|
if(src->type->etype == bytetype->etype)
|
|
return OARRAYBYTESTR;
|
|
if(src->type->etype == runetype->etype)
|
|
return OARRAYRUNESTR;
|
|
}
|
|
|
|
// 7. src is a string and dst is []byte or []rune.
|
|
// String to slice.
|
|
if(src->etype == TSTRING && isslice(dst)) {
|
|
if(dst->type->etype == bytetype->etype)
|
|
return OSTRARRAYBYTE;
|
|
if(dst->type->etype == runetype->etype)
|
|
return OSTRARRAYRUNE;
|
|
}
|
|
|
|
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
|
|
if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR)
|
|
return OCONVNOP;
|
|
|
|
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
|
|
if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR))
|
|
return OCONVNOP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Convert node n for assignment to type t.
|
|
Node*
|
|
assignconv(Node *n, Type *t, char *context)
|
|
{
|
|
int op;
|
|
Node *r, *old;
|
|
char *why;
|
|
|
|
if(n == N || n->type == T || n->type->broke)
|
|
return n;
|
|
|
|
old = n;
|
|
old->diag++; // silence errors about n; we'll issue one below
|
|
defaultlit(&n, t);
|
|
old->diag--;
|
|
if(t->etype == TBLANK)
|
|
return n;
|
|
|
|
// Convert ideal bool from comparison to plain bool
|
|
// if the next step is non-bool (like interface{}).
|
|
if(n->type == idealbool && t->etype != TBOOL) {
|
|
if(n->op == ONAME || n->op == OLITERAL) {
|
|
r = nod(OCONVNOP, n, N);
|
|
r->type = types[TBOOL];
|
|
r->typecheck = 1;
|
|
r->implicit = 1;
|
|
n = r;
|
|
}
|
|
}
|
|
|
|
if(eqtype(n->type, t))
|
|
return n;
|
|
|
|
op = assignop(n->type, t, &why);
|
|
if(op == 0) {
|
|
yyerror("cannot use %lN as type %T in %s%s", n, t, context, why);
|
|
op = OCONV;
|
|
}
|
|
|
|
r = nod(op, n, N);
|
|
r->type = t;
|
|
r->typecheck = 1;
|
|
r->implicit = 1;
|
|
r->orig = n->orig;
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
subtype(Type **stp, Type *t, int d)
|
|
{
|
|
Type *st;
|
|
|
|
loop:
|
|
st = *stp;
|
|
if(st == T)
|
|
return 0;
|
|
|
|
d++;
|
|
if(d >= 10)
|
|
return 0;
|
|
|
|
switch(st->etype) {
|
|
default:
|
|
return 0;
|
|
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TCHAN:
|
|
case TARRAY:
|
|
stp = &st->type;
|
|
goto loop;
|
|
|
|
case TANY:
|
|
if(!st->copyany)
|
|
return 0;
|
|
*stp = t;
|
|
break;
|
|
|
|
case TMAP:
|
|
if(subtype(&st->down, t, d))
|
|
break;
|
|
stp = &st->type;
|
|
goto loop;
|
|
|
|
case TFUNC:
|
|
for(;;) {
|
|
if(subtype(&st->type, t, d))
|
|
break;
|
|
if(subtype(&st->type->down->down, t, d))
|
|
break;
|
|
if(subtype(&st->type->down, t, d))
|
|
break;
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
for(st=st->type; st!=T; st=st->down)
|
|
if(subtype(&st->type, t, d))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Is this a 64-bit type?
|
|
*/
|
|
int
|
|
is64(Type *t)
|
|
{
|
|
if(t == T)
|
|
return 0;
|
|
switch(simtype[t->etype]) {
|
|
case TINT64:
|
|
case TUINT64:
|
|
case TPTR64:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Is a conversion between t1 and t2 a no-op?
|
|
*/
|
|
int
|
|
noconv(Type *t1, Type *t2)
|
|
{
|
|
int e1, e2;
|
|
|
|
e1 = simtype[t1->etype];
|
|
e2 = simtype[t2->etype];
|
|
|
|
switch(e1) {
|
|
case TINT8:
|
|
case TUINT8:
|
|
return e2 == TINT8 || e2 == TUINT8;
|
|
|
|
case TINT16:
|
|
case TUINT16:
|
|
return e2 == TINT16 || e2 == TUINT16;
|
|
|
|
case TINT32:
|
|
case TUINT32:
|
|
case TPTR32:
|
|
return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32;
|
|
|
|
case TINT64:
|
|
case TUINT64:
|
|
case TPTR64:
|
|
return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64;
|
|
|
|
case TFLOAT32:
|
|
return e2 == TFLOAT32;
|
|
|
|
case TFLOAT64:
|
|
return e2 == TFLOAT64;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
argtype(Node *on, Type *t)
|
|
{
|
|
dowidth(t);
|
|
if(!subtype(&on->type, t, 0))
|
|
fatal("argtype: failed %N %T\n", on, t);
|
|
}
|
|
|
|
Type*
|
|
shallow(Type *t)
|
|
{
|
|
Type *nt;
|
|
|
|
if(t == T)
|
|
return T;
|
|
nt = typ(0);
|
|
*nt = *t;
|
|
if(t->orig == t)
|
|
nt->orig = nt;
|
|
return nt;
|
|
}
|
|
|
|
static Type*
|
|
deep(Type *t)
|
|
{
|
|
Type *nt, *xt;
|
|
|
|
if(t == T)
|
|
return T;
|
|
|
|
switch(t->etype) {
|
|
default:
|
|
nt = t; // share from here down
|
|
break;
|
|
|
|
case TANY:
|
|
nt = shallow(t);
|
|
nt->copyany = 1;
|
|
break;
|
|
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TCHAN:
|
|
case TARRAY:
|
|
nt = shallow(t);
|
|
nt->type = deep(t->type);
|
|
break;
|
|
|
|
case TMAP:
|
|
nt = shallow(t);
|
|
nt->down = deep(t->down);
|
|
nt->type = deep(t->type);
|
|
break;
|
|
|
|
case TFUNC:
|
|
nt = shallow(t);
|
|
nt->type = deep(t->type);
|
|
nt->type->down = deep(t->type->down);
|
|
nt->type->down->down = deep(t->type->down->down);
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
nt = shallow(t);
|
|
nt->type = shallow(t->type);
|
|
xt = nt->type;
|
|
|
|
for(t=t->type; t!=T; t=t->down) {
|
|
xt->type = deep(t->type);
|
|
xt->down = shallow(t->down);
|
|
xt = xt->down;
|
|
}
|
|
break;
|
|
}
|
|
return nt;
|
|
}
|
|
|
|
Node*
|
|
syslook(char *name, int copy)
|
|
{
|
|
Sym *s;
|
|
Node *n;
|
|
|
|
s = pkglookup(name, runtimepkg);
|
|
if(s == S || s->def == N)
|
|
fatal("syslook: can't find runtime.%s", name);
|
|
|
|
if(!copy)
|
|
return s->def;
|
|
|
|
n = nod(0, N, N);
|
|
*n = *s->def;
|
|
n->type = deep(s->def->type);
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* compute a hash value for type t.
|
|
* if t is a method type, ignore the receiver
|
|
* so that the hash can be used in interface checks.
|
|
* %T already contains
|
|
* all the necessary logic to generate a representation
|
|
* of the type that completely describes it.
|
|
* using smprint here avoids duplicating that code.
|
|
* using md5 here is overkill, but i got tired of
|
|
* accidental collisions making the runtime think
|
|
* two types are equal when they really aren't.
|
|
*/
|
|
uint32
|
|
typehash(Type *t)
|
|
{
|
|
char *p;
|
|
MD5 d;
|
|
|
|
if(t->thistuple) {
|
|
// hide method receiver from Tpretty
|
|
t->thistuple = 0;
|
|
p = smprint("%-uT", t);
|
|
t->thistuple = 1;
|
|
} else
|
|
p = smprint("%-uT", t);
|
|
//print("typehash: %s\n", p);
|
|
md5reset(&d);
|
|
md5write(&d, (uchar*)p, strlen(p));
|
|
free(p);
|
|
return md5sum(&d);
|
|
}
|
|
|
|
Type*
|
|
ptrto(Type *t)
|
|
{
|
|
Type *t1;
|
|
|
|
if(tptr == 0)
|
|
fatal("ptrto: no tptr");
|
|
t1 = typ(tptr);
|
|
t1->type = t;
|
|
t1->width = widthptr;
|
|
t1->align = widthptr;
|
|
return t1;
|
|
}
|
|
|
|
void
|
|
frame(int context)
|
|
{
|
|
char *p;
|
|
NodeList *l;
|
|
Node *n;
|
|
int flag;
|
|
|
|
p = "stack";
|
|
l = nil;
|
|
if(curfn)
|
|
l = curfn->dcl;
|
|
if(context) {
|
|
p = "external";
|
|
l = externdcl;
|
|
}
|
|
|
|
flag = 1;
|
|
for(; l; l=l->next) {
|
|
n = l->n;
|
|
switch(n->op) {
|
|
case ONAME:
|
|
if(flag)
|
|
print("--- %s frame ---\n", p);
|
|
print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type);
|
|
flag = 0;
|
|
break;
|
|
|
|
case OTYPE:
|
|
if(flag)
|
|
print("--- %s frame ---\n", p);
|
|
print("%O %T\n", n->op, n->type);
|
|
flag = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* calculate sethi/ullman number
|
|
* roughly how many registers needed to
|
|
* compile a node. used to compile the
|
|
* hardest side first to minimize registers.
|
|
*/
|
|
void
|
|
ullmancalc(Node *n)
|
|
{
|
|
int ul, ur;
|
|
|
|
if(n == N)
|
|
return;
|
|
|
|
if(n->ninit != nil) {
|
|
ul = UINF;
|
|
goto out;
|
|
}
|
|
|
|
switch(n->op) {
|
|
case OREGISTER:
|
|
case OLITERAL:
|
|
case ONAME:
|
|
ul = 1;
|
|
if(n->class == PPARAMREF || (n->class & PHEAP))
|
|
ul++;
|
|
goto out;
|
|
case OCALL:
|
|
case OCALLFUNC:
|
|
case OCALLMETH:
|
|
case OCALLINTER:
|
|
ul = UINF;
|
|
goto out;
|
|
case OANDAND:
|
|
case OOROR:
|
|
// hard with race detector
|
|
if(flag_race) {
|
|
ul = UINF;
|
|
goto out;
|
|
}
|
|
}
|
|
ul = 1;
|
|
if(n->left != N)
|
|
ul = n->left->ullman;
|
|
ur = 1;
|
|
if(n->right != N)
|
|
ur = n->right->ullman;
|
|
if(ul == ur)
|
|
ul += 1;
|
|
if(ur > ul)
|
|
ul = ur;
|
|
|
|
out:
|
|
if(ul > 200)
|
|
ul = 200; // clamp to uchar with room to grow
|
|
n->ullman = ul;
|
|
}
|
|
|
|
void
|
|
badtype(int o, Type *tl, Type *tr)
|
|
{
|
|
Fmt fmt;
|
|
char *s;
|
|
|
|
fmtstrinit(&fmt);
|
|
if(tl != T)
|
|
fmtprint(&fmt, "\n %T", tl);
|
|
if(tr != T)
|
|
fmtprint(&fmt, "\n %T", tr);
|
|
|
|
// common mistake: *struct and *interface.
|
|
if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) {
|
|
if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER)
|
|
fmtprint(&fmt, "\n (*struct vs *interface)");
|
|
else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT)
|
|
fmtprint(&fmt, "\n (*interface vs *struct)");
|
|
}
|
|
s = fmtstrflush(&fmt);
|
|
yyerror("illegal types for operand: %O%s", o, s);
|
|
}
|
|
|
|
/*
|
|
* iterator to walk a structure declaration
|
|
*/
|
|
Type*
|
|
structfirst(Iter *s, Type **nn)
|
|
{
|
|
Type *n, *t;
|
|
|
|
n = *nn;
|
|
if(n == T)
|
|
goto bad;
|
|
|
|
switch(n->etype) {
|
|
default:
|
|
goto bad;
|
|
|
|
case TSTRUCT:
|
|
case TINTER:
|
|
case TFUNC:
|
|
break;
|
|
}
|
|
|
|
t = n->type;
|
|
if(t == T)
|
|
goto rnil;
|
|
|
|
if(t->etype != TFIELD)
|
|
fatal("structfirst: not field %T", t);
|
|
|
|
s->t = t;
|
|
return t;
|
|
|
|
bad:
|
|
fatal("structfirst: not struct %T", n);
|
|
|
|
rnil:
|
|
return T;
|
|
}
|
|
|
|
Type*
|
|
structnext(Iter *s)
|
|
{
|
|
Type *n, *t;
|
|
|
|
n = s->t;
|
|
t = n->down;
|
|
if(t == T)
|
|
goto rnil;
|
|
|
|
if(t->etype != TFIELD)
|
|
goto bad;
|
|
|
|
s->t = t;
|
|
return t;
|
|
|
|
bad:
|
|
fatal("structnext: not struct %T", n);
|
|
|
|
rnil:
|
|
return T;
|
|
}
|
|
|
|
/*
|
|
* iterator to this and inargs in a function
|
|
*/
|
|
Type*
|
|
funcfirst(Iter *s, Type *t)
|
|
{
|
|
Type *fp;
|
|
|
|
if(t == T)
|
|
goto bad;
|
|
|
|
if(t->etype != TFUNC)
|
|
goto bad;
|
|
|
|
s->tfunc = t;
|
|
s->done = 0;
|
|
fp = structfirst(s, getthis(t));
|
|
if(fp == T) {
|
|
s->done = 1;
|
|
fp = structfirst(s, getinarg(t));
|
|
}
|
|
return fp;
|
|
|
|
bad:
|
|
fatal("funcfirst: not func %T", t);
|
|
return T;
|
|
}
|
|
|
|
Type*
|
|
funcnext(Iter *s)
|
|
{
|
|
Type *fp;
|
|
|
|
fp = structnext(s);
|
|
if(fp == T && !s->done) {
|
|
s->done = 1;
|
|
fp = structfirst(s, getinarg(s->tfunc));
|
|
}
|
|
return fp;
|
|
}
|
|
|
|
Type**
|
|
getthis(Type *t)
|
|
{
|
|
if(t->etype != TFUNC)
|
|
fatal("getthis: not a func %T", t);
|
|
return &t->type;
|
|
}
|
|
|
|
Type**
|
|
getoutarg(Type *t)
|
|
{
|
|
if(t->etype != TFUNC)
|
|
fatal("getoutarg: not a func %T", t);
|
|
return &t->type->down;
|
|
}
|
|
|
|
Type**
|
|
getinarg(Type *t)
|
|
{
|
|
if(t->etype != TFUNC)
|
|
fatal("getinarg: not a func %T", t);
|
|
return &t->type->down->down;
|
|
}
|
|
|
|
Type*
|
|
getthisx(Type *t)
|
|
{
|
|
return *getthis(t);
|
|
}
|
|
|
|
Type*
|
|
getoutargx(Type *t)
|
|
{
|
|
return *getoutarg(t);
|
|
}
|
|
|
|
Type*
|
|
getinargx(Type *t)
|
|
{
|
|
return *getinarg(t);
|
|
}
|
|
|
|
/*
|
|
* return !(op)
|
|
* eg == <=> !=
|
|
*/
|
|
int
|
|
brcom(int a)
|
|
{
|
|
switch(a) {
|
|
case OEQ: return ONE;
|
|
case ONE: return OEQ;
|
|
case OLT: return OGE;
|
|
case OGT: return OLE;
|
|
case OLE: return OGT;
|
|
case OGE: return OLT;
|
|
}
|
|
fatal("brcom: no com for %A\n", a);
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
* return reverse(op)
|
|
* eg a op b <=> b r(op) a
|
|
*/
|
|
int
|
|
brrev(int a)
|
|
{
|
|
switch(a) {
|
|
case OEQ: return OEQ;
|
|
case ONE: return ONE;
|
|
case OLT: return OGT;
|
|
case OGT: return OLT;
|
|
case OLE: return OGE;
|
|
case OGE: return OLE;
|
|
}
|
|
fatal("brcom: no rev for %A\n", a);
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
* return side effect-free n, appending side effects to init.
|
|
* result is assignable if n is.
|
|
*/
|
|
Node*
|
|
safeexpr(Node *n, NodeList **init)
|
|
{
|
|
Node *l;
|
|
Node *r;
|
|
Node *a;
|
|
|
|
if(n == N)
|
|
return N;
|
|
|
|
if(n->ninit) {
|
|
walkstmtlist(n->ninit);
|
|
*init = concat(*init, n->ninit);
|
|
n->ninit = nil;
|
|
}
|
|
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OLITERAL:
|
|
return n;
|
|
|
|
case ODOT:
|
|
l = safeexpr(n->left, init);
|
|
if(l == n->left)
|
|
return n;
|
|
r = nod(OXXX, N, N);
|
|
*r = *n;
|
|
r->left = l;
|
|
typecheck(&r, Erv);
|
|
walkexpr(&r, init);
|
|
return r;
|
|
|
|
case ODOTPTR:
|
|
case OIND:
|
|
l = safeexpr(n->left, init);
|
|
if(l == n->left)
|
|
return n;
|
|
a = nod(OXXX, N, N);
|
|
*a = *n;
|
|
a->left = l;
|
|
walkexpr(&a, init);
|
|
return a;
|
|
|
|
case OINDEX:
|
|
case OINDEXMAP:
|
|
l = safeexpr(n->left, init);
|
|
r = safeexpr(n->right, init);
|
|
if(l == n->left && r == n->right)
|
|
return n;
|
|
a = nod(OXXX, N, N);
|
|
*a = *n;
|
|
a->left = l;
|
|
a->right = r;
|
|
walkexpr(&a, init);
|
|
return a;
|
|
}
|
|
|
|
// make a copy; must not be used as an lvalue
|
|
if(islvalue(n))
|
|
fatal("missing lvalue case in safeexpr: %N", n);
|
|
return cheapexpr(n, init);
|
|
}
|
|
|
|
Node*
|
|
copyexpr(Node *n, Type *t, NodeList **init)
|
|
{
|
|
Node *a, *l;
|
|
|
|
l = temp(t);
|
|
a = nod(OAS, l, n);
|
|
typecheck(&a, Etop);
|
|
walkexpr(&a, init);
|
|
*init = list(*init, a);
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
* return side-effect free and cheap n, appending side effects to init.
|
|
* result may not be assignable.
|
|
*/
|
|
Node*
|
|
cheapexpr(Node *n, NodeList **init)
|
|
{
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OLITERAL:
|
|
return n;
|
|
}
|
|
|
|
return copyexpr(n, n->type, init);
|
|
}
|
|
|
|
/*
|
|
* return n in a local variable of type t if it is not already.
|
|
* the value is guaranteed not to change except by direct
|
|
* assignment to it.
|
|
*/
|
|
Node*
|
|
localexpr(Node *n, Type *t, NodeList **init)
|
|
{
|
|
if(n->op == ONAME && !n->addrtaken &&
|
|
(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
|
|
convertop(n->type, t, nil) == OCONVNOP)
|
|
return n;
|
|
|
|
return copyexpr(n, t, init);
|
|
}
|
|
|
|
void
|
|
setmaxarg(Type *t)
|
|
{
|
|
int64 w;
|
|
|
|
dowidth(t);
|
|
w = t->argwid;
|
|
if(t->argwid >= MAXWIDTH)
|
|
fatal("bad argwid %T", t);
|
|
if(w > maxarg)
|
|
maxarg = w;
|
|
}
|
|
|
|
/*
|
|
* unicode-aware case-insensitive strcmp
|
|
*/
|
|
|
|
static int
|
|
ucistrcmp(char *p, char *q)
|
|
{
|
|
Rune rp, rq;
|
|
|
|
while(*p || *q) {
|
|
if(*p == 0)
|
|
return +1;
|
|
if(*q == 0)
|
|
return -1;
|
|
p += chartorune(&rp, p);
|
|
q += chartorune(&rq, q);
|
|
rp = tolowerrune(rp);
|
|
rq = tolowerrune(rq);
|
|
if(rp < rq)
|
|
return -1;
|
|
if(rp > rq)
|
|
return +1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* code to resolve elided DOTs
|
|
* in embedded types
|
|
*/
|
|
|
|
// search depth 0 --
|
|
// return count of fields+methods
|
|
// found with a given name
|
|
static int
|
|
lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
|
|
{
|
|
Type *f, *u;
|
|
int c;
|
|
|
|
u = t;
|
|
if(isptr[u->etype])
|
|
u = u->type;
|
|
|
|
c = 0;
|
|
if(u->etype == TSTRUCT || u->etype == TINTER) {
|
|
for(f=u->type; f!=T; f=f->down)
|
|
if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)) {
|
|
if(save)
|
|
*save = f;
|
|
c++;
|
|
}
|
|
}
|
|
u = methtype(t, 0);
|
|
if(u != T) {
|
|
for(f=u->method; f!=T; f=f->down)
|
|
if(f->embedded == 0 && (f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))) {
|
|
if(save)
|
|
*save = f;
|
|
c++;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// search depth d for field/method s --
|
|
// return count of fields+methods
|
|
// found at search depth.
|
|
// answer is in dotlist array and
|
|
// count of number of ways is returned.
|
|
int
|
|
adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase)
|
|
{
|
|
Type *f, *u;
|
|
int c, a;
|
|
|
|
if(t->trecur)
|
|
return 0;
|
|
t->trecur = 1;
|
|
|
|
if(d == 0) {
|
|
c = lookdot0(s, t, save, ignorecase);
|
|
goto out;
|
|
}
|
|
|
|
c = 0;
|
|
u = t;
|
|
if(isptr[u->etype])
|
|
u = u->type;
|
|
if(u->etype != TSTRUCT && u->etype != TINTER)
|
|
goto out;
|
|
|
|
d--;
|
|
for(f=u->type; f!=T; f=f->down) {
|
|
if(!f->embedded)
|
|
continue;
|
|
if(f->sym == S)
|
|
continue;
|
|
a = adddot1(s, f->type, d, save, ignorecase);
|
|
if(a != 0 && c == 0)
|
|
dotlist[d].field = f;
|
|
c += a;
|
|
}
|
|
|
|
out:
|
|
t->trecur = 0;
|
|
return c;
|
|
}
|
|
|
|
// in T.field
|
|
// find missing fields that
|
|
// will give shortest unique addressing.
|
|
// modify the tree with missing type names.
|
|
Node*
|
|
adddot(Node *n)
|
|
{
|
|
Type *t;
|
|
Sym *s;
|
|
int c, d;
|
|
|
|
typecheck(&n->left, Etype|Erv);
|
|
t = n->left->type;
|
|
if(t == T)
|
|
goto ret;
|
|
|
|
if(n->left->op == OTYPE)
|
|
goto ret;
|
|
|
|
if(n->right->op != ONAME)
|
|
goto ret;
|
|
s = n->right->sym;
|
|
if(s == S)
|
|
goto ret;
|
|
|
|
for(d=0; d<nelem(dotlist); d++) {
|
|
c = adddot1(s, t, d, nil, 0);
|
|
if(c > 0)
|
|
goto out;
|
|
}
|
|
goto ret;
|
|
|
|
out:
|
|
if(c > 1) {
|
|
yyerror("ambiguous selector %N", n);
|
|
n->left = N;
|
|
return n;
|
|
}
|
|
|
|
// rebuild elided dots
|
|
for(c=d-1; c>=0; c--)
|
|
n->left = nod(ODOT, n->left, newname(dotlist[c].field->sym));
|
|
ret:
|
|
return n;
|
|
}
|
|
|
|
|
|
/*
|
|
* code to help generate trampoline
|
|
* functions for methods on embedded
|
|
* subtypes.
|
|
* these are approx the same as
|
|
* the corresponding adddot routines
|
|
* except that they expect to be called
|
|
* with unique tasks and they return
|
|
* the actual methods.
|
|
*/
|
|
|
|
typedef struct Symlink Symlink;
|
|
struct Symlink
|
|
{
|
|
Type* field;
|
|
uchar good;
|
|
uchar followptr;
|
|
Symlink* link;
|
|
};
|
|
static Symlink* slist;
|
|
|
|
static void
|
|
expand0(Type *t, int followptr)
|
|
{
|
|
Type *f, *u;
|
|
Symlink *sl;
|
|
|
|
u = t;
|
|
if(isptr[u->etype]) {
|
|
followptr = 1;
|
|
u = u->type;
|
|
}
|
|
|
|
if(u->etype == TINTER) {
|
|
for(f=u->type; f!=T; f=f->down) {
|
|
if(f->sym->flags & SymUniq)
|
|
continue;
|
|
f->sym->flags |= SymUniq;
|
|
sl = mal(sizeof(*sl));
|
|
sl->field = f;
|
|
sl->link = slist;
|
|
sl->followptr = followptr;
|
|
slist = sl;
|
|
}
|
|
return;
|
|
}
|
|
|
|
u = methtype(t, 0);
|
|
if(u != T) {
|
|
for(f=u->method; f!=T; f=f->down) {
|
|
if(f->sym->flags & SymUniq)
|
|
continue;
|
|
f->sym->flags |= SymUniq;
|
|
sl = mal(sizeof(*sl));
|
|
sl->field = f;
|
|
sl->link = slist;
|
|
sl->followptr = followptr;
|
|
slist = sl;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
expand1(Type *t, int d, int followptr)
|
|
{
|
|
Type *f, *u;
|
|
|
|
if(t->trecur)
|
|
return;
|
|
if(d == 0)
|
|
return;
|
|
t->trecur = 1;
|
|
|
|
if(d != nelem(dotlist)-1)
|
|
expand0(t, followptr);
|
|
|
|
u = t;
|
|
if(isptr[u->etype]) {
|
|
followptr = 1;
|
|
u = u->type;
|
|
}
|
|
if(u->etype != TSTRUCT && u->etype != TINTER)
|
|
goto out;
|
|
|
|
for(f=u->type; f!=T; f=f->down) {
|
|
if(!f->embedded)
|
|
continue;
|
|
if(f->sym == S)
|
|
continue;
|
|
expand1(f->type, d-1, followptr);
|
|
}
|
|
|
|
out:
|
|
t->trecur = 0;
|
|
}
|
|
|
|
void
|
|
expandmeth(Type *t)
|
|
{
|
|
Symlink *sl;
|
|
Type *f;
|
|
int c, d;
|
|
|
|
if(t == T || t->xmethod != nil)
|
|
return;
|
|
|
|
// mark top-level method symbols
|
|
// so that expand1 doesn't consider them.
|
|
for(f=t->method; f != nil; f=f->down)
|
|
f->sym->flags |= SymUniq;
|
|
|
|
// generate all reachable methods
|
|
slist = nil;
|
|
expand1(t, nelem(dotlist)-1, 0);
|
|
|
|
// check each method to be uniquely reachable
|
|
for(sl=slist; sl!=nil; sl=sl->link) {
|
|
sl->field->sym->flags &= ~SymUniq;
|
|
for(d=0; d<nelem(dotlist); d++) {
|
|
c = adddot1(sl->field->sym, t, d, &f, 0);
|
|
if(c == 0)
|
|
continue;
|
|
if(c == 1) {
|
|
// addot1 may have dug out arbitrary fields, we only want methods.
|
|
if(f->type->etype == TFUNC && f->type->thistuple > 0) {
|
|
sl->good = 1;
|
|
sl->field = f;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(f=t->method; f != nil; f=f->down)
|
|
f->sym->flags &= ~SymUniq;
|
|
|
|
t->xmethod = t->method;
|
|
for(sl=slist; sl!=nil; sl=sl->link) {
|
|
if(sl->good) {
|
|
// add it to the base type method list
|
|
f = typ(TFIELD);
|
|
*f = *sl->field;
|
|
f->embedded = 1; // needs a trampoline
|
|
if(sl->followptr)
|
|
f->embedded = 2;
|
|
f->down = t->xmethod;
|
|
t->xmethod = f;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Given funarg struct list, return list of ODCLFIELD Node fn args.
|
|
*/
|
|
static NodeList*
|
|
structargs(Type **tl, int mustname)
|
|
{
|
|
Iter savet;
|
|
Node *a, *n;
|
|
NodeList *args;
|
|
Type *t;
|
|
char buf[100];
|
|
int gen;
|
|
|
|
args = nil;
|
|
gen = 0;
|
|
for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) {
|
|
n = N;
|
|
if(mustname && (t->sym == nil || strcmp(t->sym->name, "_") == 0)) {
|
|
// invent a name so that we can refer to it in the trampoline
|
|
snprint(buf, sizeof buf, ".anon%d", gen++);
|
|
n = newname(lookup(buf));
|
|
} else if(t->sym)
|
|
n = newname(t->sym);
|
|
a = nod(ODCLFIELD, n, typenod(t->type));
|
|
a->isddd = t->isddd;
|
|
if(n != N)
|
|
n->isddd = t->isddd;
|
|
args = list(args, a);
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/*
|
|
* Generate a wrapper function to convert from
|
|
* a receiver of type T to a receiver of type U.
|
|
* That is,
|
|
*
|
|
* func (t T) M() {
|
|
* ...
|
|
* }
|
|
*
|
|
* already exists; this function generates
|
|
*
|
|
* func (u U) M() {
|
|
* u.M()
|
|
* }
|
|
*
|
|
* where the types T and U are such that u.M() is valid
|
|
* and calls the T.M method.
|
|
* The resulting function is for use in method tables.
|
|
*
|
|
* rcvr - U
|
|
* method - M func (t T)(), a TFIELD type struct
|
|
* newnam - the eventual mangled name of this function
|
|
*/
|
|
void
|
|
genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
|
|
{
|
|
Node *this, *fn, *call, *n, *t, *pad, *dot, *as;
|
|
NodeList *l, *args, *in, *out;
|
|
Type *tpad, *methodrcvr;
|
|
int isddd;
|
|
Val v;
|
|
|
|
if(0 && debug['r'])
|
|
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
|
|
rcvr, method, newnam);
|
|
|
|
lineno = 1; // less confusing than end of input
|
|
|
|
dclcontext = PEXTERN;
|
|
markdcl();
|
|
|
|
this = nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr));
|
|
this->left->ntype = this->right;
|
|
in = structargs(getinarg(method->type), 1);
|
|
out = structargs(getoutarg(method->type), 0);
|
|
|
|
t = nod(OTFUNC, N, N);
|
|
l = list1(this);
|
|
if(iface && rcvr->width < types[tptr]->width) {
|
|
// Building method for interface table and receiver
|
|
// is smaller than the single pointer-sized word
|
|
// that the interface call will pass in.
|
|
// Add a dummy padding argument after the
|
|
// receiver to make up the difference.
|
|
tpad = typ(TARRAY);
|
|
tpad->type = types[TUINT8];
|
|
tpad->bound = types[tptr]->width - rcvr->width;
|
|
pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad));
|
|
l = list(l, pad);
|
|
}
|
|
t->list = concat(l, in);
|
|
t->rlist = out;
|
|
|
|
fn = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(newnam);
|
|
fn->nname->defn = fn;
|
|
fn->nname->ntype = t;
|
|
declare(fn->nname, PFUNC);
|
|
funchdr(fn);
|
|
|
|
// arg list
|
|
args = nil;
|
|
isddd = 0;
|
|
for(l=in; l; l=l->next) {
|
|
args = list(args, l->n->left);
|
|
isddd = l->n->left->isddd;
|
|
}
|
|
|
|
methodrcvr = getthisx(method->type)->type->type;
|
|
|
|
// generate nil pointer check for better error
|
|
if(isptr[rcvr->etype] && rcvr->type == methodrcvr) {
|
|
// generating wrapper from *T to T.
|
|
n = nod(OIF, N, N);
|
|
n->ntest = nod(OEQ, this->left, nodnil());
|
|
// these strings are already in the reflect tables,
|
|
// so no space cost to use them here.
|
|
l = nil;
|
|
v.ctype = CTSTR;
|
|
v.u.sval = strlit(rcvr->type->sym->pkg->name); // package name
|
|
l = list(l, nodlit(v));
|
|
v.u.sval = strlit(rcvr->type->sym->name); // type name
|
|
l = list(l, nodlit(v));
|
|
v.u.sval = strlit(method->sym->name);
|
|
l = list(l, nodlit(v)); // method name
|
|
call = nod(OCALL, syslook("panicwrap", 0), N);
|
|
call->list = l;
|
|
n->nbody = list1(call);
|
|
fn->nbody = list(fn->nbody, n);
|
|
}
|
|
|
|
dot = adddot(nod(OXDOT, this->left, newname(method->sym)));
|
|
|
|
// generate call
|
|
if(!flag_race && isptr[rcvr->etype] && isptr[methodrcvr->etype] && method->embedded && !isifacemethod(method->type)) {
|
|
// generate tail call: adjust pointer receiver and jump to embedded method.
|
|
dot = dot->left; // skip final .M
|
|
if(!isptr[dotlist[0].field->type->etype])
|
|
dot = nod(OADDR, dot, N);
|
|
as = nod(OAS, this->left, nod(OCONVNOP, dot, N));
|
|
as->right->type = rcvr;
|
|
fn->nbody = list(fn->nbody, as);
|
|
n = nod(ORETJMP, N, N);
|
|
n->left = newname(methodsym(method->sym, methodrcvr, 0));
|
|
fn->nbody = list(fn->nbody, n);
|
|
} else {
|
|
call = nod(OCALL, dot, N);
|
|
call->list = args;
|
|
call->isddd = isddd;
|
|
if(method->type->outtuple > 0) {
|
|
n = nod(ORETURN, N, N);
|
|
n->list = list1(call);
|
|
call = n;
|
|
}
|
|
fn->nbody = list(fn->nbody, call);
|
|
}
|
|
|
|
if(0 && debug['r'])
|
|
dumplist("genwrapper body", fn->nbody);
|
|
|
|
funcbody(fn);
|
|
curfn = fn;
|
|
// wrappers where T is anonymous (struct{ NamedType }) can be duplicated.
|
|
if(rcvr->etype == TSTRUCT || isptr[rcvr->etype] && rcvr->type->etype == TSTRUCT)
|
|
fn->dupok = 1;
|
|
typecheck(&fn, Etop);
|
|
typechecklist(fn->nbody, Etop);
|
|
inlcalls(fn);
|
|
curfn = nil;
|
|
funccompile(fn, 0);
|
|
}
|
|
|
|
static Node*
|
|
hashmem(Type *t)
|
|
{
|
|
Node *tfn, *n;
|
|
Sym *sym;
|
|
|
|
sym = pkglookup("memhash", runtimepkg);
|
|
|
|
n = newname(sym);
|
|
n->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR]))));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
|
|
typecheck(&tfn, Etype);
|
|
n->type = tfn->type;
|
|
return n;
|
|
}
|
|
|
|
static Node*
|
|
hashfor(Type *t)
|
|
{
|
|
int a;
|
|
Sym *sym;
|
|
Node *tfn, *n;
|
|
|
|
a = algtype1(t, nil);
|
|
switch(a) {
|
|
case AMEM:
|
|
return hashmem(t);
|
|
case AINTER:
|
|
sym = pkglookup("interhash", runtimepkg);
|
|
break;
|
|
case ANILINTER:
|
|
sym = pkglookup("nilinterhash", runtimepkg);
|
|
break;
|
|
case ASTRING:
|
|
sym = pkglookup("strhash", runtimepkg);
|
|
break;
|
|
case AFLOAT32:
|
|
sym = pkglookup("f32hash", runtimepkg);
|
|
break;
|
|
case AFLOAT64:
|
|
sym = pkglookup("f64hash", runtimepkg);
|
|
break;
|
|
case ACPLX64:
|
|
sym = pkglookup("c64hash", runtimepkg);
|
|
break;
|
|
case ACPLX128:
|
|
sym = pkglookup("c128hash", runtimepkg);
|
|
break;
|
|
default:
|
|
sym = typesymprefix(".hash", t);
|
|
break;
|
|
}
|
|
|
|
n = newname(sym);
|
|
n->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR]))));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
|
|
typecheck(&tfn, Etype);
|
|
n->type = tfn->type;
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Generate a helper function to compute the hash of a value of type t.
|
|
*/
|
|
void
|
|
genhash(Sym *sym, Type *t)
|
|
{
|
|
Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn;
|
|
Node *hashel;
|
|
Type *first, *t1;
|
|
int old_safemode;
|
|
int64 size, mul, offend;
|
|
|
|
if(debug['r'])
|
|
print("genhash %S %T\n", sym, t);
|
|
|
|
lineno = 1; // less confusing than end of input
|
|
dclcontext = PEXTERN;
|
|
markdcl();
|
|
|
|
// func sym(h *uintptr, s uintptr, p *T)
|
|
fn = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(sym);
|
|
fn->nname->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
fn->nname->ntype = tfn;
|
|
|
|
n = nod(ODCLFIELD, newname(lookup("h")), typenod(ptrto(types[TUINTPTR])));
|
|
tfn->list = list(tfn->list, n);
|
|
nh = n->left;
|
|
n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
|
|
tfn->list = list(tfn->list, n);
|
|
n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
|
|
tfn->list = list(tfn->list, n);
|
|
np = n->left;
|
|
|
|
funchdr(fn);
|
|
typecheck(&fn->nname->ntype, Etype);
|
|
|
|
// genhash is only called for types that have equality but
|
|
// cannot be handled by the standard algorithms,
|
|
// so t must be either an array or a struct.
|
|
switch(t->etype) {
|
|
default:
|
|
fatal("genhash %T", t);
|
|
case TARRAY:
|
|
if(isslice(t))
|
|
fatal("genhash %T", t);
|
|
// An array of pure memory would be handled by the
|
|
// standard algorithm, so the element type must not be
|
|
// pure memory.
|
|
hashel = hashfor(t->type);
|
|
n = nod(ORANGE, N, nod(OIND, np, N));
|
|
ni = newname(lookup("i"));
|
|
ni->type = types[TINT];
|
|
n->list = list1(ni);
|
|
n->colas = 1;
|
|
colasdefn(n->list, n);
|
|
ni = n->list->n;
|
|
|
|
// *h = *h<<3 | *h>>61
|
|
n->nbody = list(n->nbody,
|
|
nod(OAS,
|
|
nod(OIND, nh, N),
|
|
nod(OOR,
|
|
nod(OLSH, nod(OIND, nh, N), nodintconst(3)),
|
|
nod(ORSH, nod(OIND, nh, N), nodintconst(widthptr*8-3)))));
|
|
|
|
// *h *= mul
|
|
// Same multipliers as in runtime.memhash.
|
|
if(widthptr == 4)
|
|
mul = 3267000013LL;
|
|
else
|
|
mul = 23344194077549503LL;
|
|
n->nbody = list(n->nbody,
|
|
nod(OAS,
|
|
nod(OIND, nh, N),
|
|
nod(OMUL, nod(OIND, nh, N), nodintconst(mul))));
|
|
|
|
// hashel(h, sizeof(p[i]), &p[i])
|
|
call = nod(OCALL, hashel, N);
|
|
call->list = list(call->list, nh);
|
|
call->list = list(call->list, nodintconst(t->type->width));
|
|
nx = nod(OINDEX, np, ni);
|
|
nx->bounded = 1;
|
|
na = nod(OADDR, nx, N);
|
|
na->etype = 1; // no escape to heap
|
|
call->list = list(call->list, na);
|
|
n->nbody = list(n->nbody, call);
|
|
|
|
fn->nbody = list(fn->nbody, n);
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
// Walk the struct using memhash for runs of AMEM
|
|
// and calling specific hash functions for the others.
|
|
first = T;
|
|
offend = 0;
|
|
for(t1=t->type;; t1=t1->down) {
|
|
if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
|
|
offend = t1->width + t1->type->width;
|
|
if(first == T)
|
|
first = t1;
|
|
// If it's a memory field but it's padded, stop here.
|
|
if(ispaddedfield(t1, t->width))
|
|
t1 = t1->down;
|
|
else
|
|
continue;
|
|
}
|
|
// Run memhash for fields up to this one.
|
|
if(first != T) {
|
|
size = offend - first->width; // first->width is offset
|
|
hashel = hashmem(first->type);
|
|
// hashel(h, size, &p.first)
|
|
call = nod(OCALL, hashel, N);
|
|
call->list = list(call->list, nh);
|
|
call->list = list(call->list, nodintconst(size));
|
|
nx = nod(OXDOT, np, newname(first->sym)); // TODO: fields from other packages?
|
|
na = nod(OADDR, nx, N);
|
|
na->etype = 1; // no escape to heap
|
|
call->list = list(call->list, na);
|
|
fn->nbody = list(fn->nbody, call);
|
|
|
|
first = T;
|
|
}
|
|
if(t1 == T)
|
|
break;
|
|
if(isblanksym(t1->sym))
|
|
continue;
|
|
|
|
// Run hash for this field.
|
|
hashel = hashfor(t1->type);
|
|
// hashel(h, size, &p.t1)
|
|
call = nod(OCALL, hashel, N);
|
|
call->list = list(call->list, nh);
|
|
call->list = list(call->list, nodintconst(t1->type->width));
|
|
nx = nod(OXDOT, np, newname(t1->sym)); // TODO: fields from other packages?
|
|
na = nod(OADDR, nx, N);
|
|
na->etype = 1; // no escape to heap
|
|
call->list = list(call->list, na);
|
|
fn->nbody = list(fn->nbody, call);
|
|
}
|
|
// make sure body is not empty.
|
|
fn->nbody = list(fn->nbody, nod(ORETURN, N, N));
|
|
break;
|
|
}
|
|
|
|
if(debug['r'])
|
|
dumplist("genhash body", fn->nbody);
|
|
|
|
funcbody(fn);
|
|
curfn = fn;
|
|
fn->dupok = 1;
|
|
typecheck(&fn, Etop);
|
|
typechecklist(fn->nbody, Etop);
|
|
curfn = nil;
|
|
|
|
// Disable safemode while compiling this code: the code we
|
|
// generate internally can refer to unsafe.Pointer.
|
|
// In this case it can happen if we need to generate an ==
|
|
// for a struct containing a reflect.Value, which itself has
|
|
// an unexported field of type unsafe.Pointer.
|
|
old_safemode = safemode;
|
|
safemode = 0;
|
|
funccompile(fn, 0);
|
|
safemode = old_safemode;
|
|
}
|
|
|
|
// Return node for
|
|
// if p.field != q.field { *eq = false; return }
|
|
static Node*
|
|
eqfield(Node *p, Node *q, Node *field, Node *eq)
|
|
{
|
|
Node *nif, *nx, *ny;
|
|
|
|
nx = nod(OXDOT, p, field);
|
|
ny = nod(OXDOT, q, field);
|
|
nif = nod(OIF, N, N);
|
|
nif->ntest = nod(ONE, nx, ny);
|
|
nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, eq, N), nodbool(0)));
|
|
nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
|
|
return nif;
|
|
}
|
|
|
|
static Node*
|
|
eqmemfunc(vlong size, Type *type)
|
|
{
|
|
char buf[30];
|
|
Node *fn;
|
|
|
|
switch(size) {
|
|
default:
|
|
fn = syslook("memequal", 1);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
case 16:
|
|
snprint(buf, sizeof buf, "memequal%d", (int)size*8);
|
|
fn = syslook(buf, 1);
|
|
break;
|
|
}
|
|
argtype(fn, type);
|
|
argtype(fn, type);
|
|
return fn;
|
|
}
|
|
|
|
// Return node for
|
|
// if memequal(size, &p.field, &q.field, eq); !*eq { return }
|
|
static Node*
|
|
eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq)
|
|
{
|
|
Node *nif, *nx, *ny, *call;
|
|
|
|
nx = nod(OADDR, nod(OXDOT, p, field), N);
|
|
nx->etype = 1; // does not escape
|
|
ny = nod(OADDR, nod(OXDOT, q, field), N);
|
|
ny->etype = 1; // does not escape
|
|
typecheck(&nx, Erv);
|
|
typecheck(&ny, Erv);
|
|
|
|
call = nod(OCALL, eqmemfunc(size, nx->type->type), N);
|
|
call->list = list(call->list, eq);
|
|
call->list = list(call->list, nodintconst(size));
|
|
call->list = list(call->list, nx);
|
|
call->list = list(call->list, ny);
|
|
|
|
nif = nod(OIF, N, N);
|
|
nif->ninit = list(nif->ninit, call);
|
|
nif->ntest = nod(ONOT, nod(OIND, eq, N), N);
|
|
nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
|
|
return nif;
|
|
}
|
|
|
|
/*
|
|
* Generate a helper function to check equality of two values of type t.
|
|
*/
|
|
void
|
|
geneq(Sym *sym, Type *t)
|
|
{
|
|
Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange;
|
|
Type *t1, *first;
|
|
int old_safemode;
|
|
int64 size;
|
|
int64 offend;
|
|
|
|
if(debug['r'])
|
|
print("geneq %S %T\n", sym, t);
|
|
|
|
lineno = 1; // less confusing than end of input
|
|
dclcontext = PEXTERN;
|
|
markdcl();
|
|
|
|
// func sym(eq *bool, s uintptr, p, q *T)
|
|
fn = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(sym);
|
|
fn->nname->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
fn->nname->ntype = tfn;
|
|
|
|
n = nod(ODCLFIELD, newname(lookup("eq")), typenod(ptrto(types[TBOOL])));
|
|
tfn->list = list(tfn->list, n);
|
|
neq = n->left;
|
|
n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
|
|
tfn->list = list(tfn->list, n);
|
|
n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
|
|
tfn->list = list(tfn->list, n);
|
|
np = n->left;
|
|
n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)));
|
|
tfn->list = list(tfn->list, n);
|
|
nq = n->left;
|
|
|
|
funchdr(fn);
|
|
|
|
// geneq is only called for types that have equality but
|
|
// cannot be handled by the standard algorithms,
|
|
// so t must be either an array or a struct.
|
|
switch(t->etype) {
|
|
default:
|
|
fatal("geneq %T", t);
|
|
case TARRAY:
|
|
if(isslice(t))
|
|
fatal("geneq %T", 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,
|
|
// each iteration would be a function call, so don't bother
|
|
// unrolling.
|
|
nrange = nod(ORANGE, N, nod(OIND, np, N));
|
|
ni = newname(lookup("i"));
|
|
ni->type = types[TINT];
|
|
nrange->list = list1(ni);
|
|
nrange->colas = 1;
|
|
colasdefn(nrange->list, nrange);
|
|
ni = nrange->list->n;
|
|
|
|
// if p[i] != q[i] { *eq = false; return }
|
|
nx = nod(OINDEX, np, ni);
|
|
nx->bounded = 1;
|
|
ny = nod(OINDEX, nq, ni);
|
|
ny->bounded = 1;
|
|
|
|
nif = nod(OIF, N, N);
|
|
nif->ntest = nod(ONE, nx, ny);
|
|
nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, neq, N), nodbool(0)));
|
|
nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
|
|
nrange->nbody = list(nrange->nbody, nif);
|
|
fn->nbody = list(fn->nbody, nrange);
|
|
|
|
// *eq = true;
|
|
fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
// Walk the struct using memequal for runs of AMEM
|
|
// and calling specific equality tests for the others.
|
|
// Skip blank-named fields.
|
|
first = T;
|
|
offend = 0;
|
|
for(t1=t->type;; t1=t1->down) {
|
|
if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
|
|
offend = t1->width + t1->type->width;
|
|
if(first == T)
|
|
first = t1;
|
|
// If it's a memory field but it's padded, stop here.
|
|
if(ispaddedfield(t1, t->width))
|
|
t1 = t1->down;
|
|
else
|
|
continue;
|
|
}
|
|
// Run memequal for fields up to this one.
|
|
// TODO(rsc): All the calls to newname are wrong for
|
|
// cross-package unexported fields.
|
|
if(first != T) {
|
|
if(first->down == t1) {
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
|
} else if(first->down->down == t1) {
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
|
first = first->down;
|
|
if(!isblanksym(first->sym))
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
|
} else {
|
|
// More than two fields: use memequal.
|
|
size = offend - first->width; // first->width is offset
|
|
fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq));
|
|
}
|
|
first = T;
|
|
}
|
|
if(t1 == T)
|
|
break;
|
|
if(isblanksym(t1->sym))
|
|
continue;
|
|
|
|
// Check this field, which is not just memory.
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq));
|
|
}
|
|
|
|
// *eq = true;
|
|
fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
|
|
break;
|
|
}
|
|
|
|
if(debug['r'])
|
|
dumplist("geneq body", fn->nbody);
|
|
|
|
funcbody(fn);
|
|
curfn = fn;
|
|
fn->dupok = 1;
|
|
typecheck(&fn, Etop);
|
|
typechecklist(fn->nbody, Etop);
|
|
curfn = nil;
|
|
|
|
// Disable safemode while compiling this code: the code we
|
|
// generate internally can refer to unsafe.Pointer.
|
|
// In this case it can happen if we need to generate an ==
|
|
// for a struct containing a reflect.Value, which itself has
|
|
// an unexported field of type unsafe.Pointer.
|
|
old_safemode = safemode;
|
|
safemode = 0;
|
|
funccompile(fn, 0);
|
|
safemode = old_safemode;
|
|
}
|
|
|
|
static Type*
|
|
ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
|
|
{
|
|
int i, c, d;
|
|
Type *m;
|
|
|
|
*followptr = 0;
|
|
|
|
if(t == T)
|
|
return T;
|
|
|
|
for(d=0; d<nelem(dotlist); d++) {
|
|
c = adddot1(s, t, d, &m, ignorecase);
|
|
if(c > 1) {
|
|
yyerror("%T.%S is ambiguous", t, s);
|
|
return T;
|
|
}
|
|
if(c == 1) {
|
|
for(i=0; i<d; i++) {
|
|
if(isptr[dotlist[i].field->type->etype]) {
|
|
*followptr = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(m->type->etype != TFUNC || m->type->thistuple == 0) {
|
|
yyerror("%T.%S is a field, not a method", t, s);
|
|
return T;
|
|
}
|
|
return m;
|
|
}
|
|
}
|
|
return T;
|
|
}
|
|
|
|
int
|
|
implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
|
|
{
|
|
Type *t0, *im, *tm, *rcvr, *imtype;
|
|
int followptr;
|
|
|
|
t0 = t;
|
|
if(t == T)
|
|
return 0;
|
|
|
|
// if this is too slow,
|
|
// could sort these first
|
|
// and then do one loop.
|
|
|
|
if(t->etype == TINTER) {
|
|
for(im=iface->type; im; im=im->down) {
|
|
for(tm=t->type; tm; tm=tm->down) {
|
|
if(tm->sym == im->sym) {
|
|
if(eqtype(tm->type, im->type))
|
|
goto found;
|
|
*m = im;
|
|
*samename = tm;
|
|
*ptr = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
*m = im;
|
|
*samename = nil;
|
|
*ptr = 0;
|
|
return 0;
|
|
found:;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
t = methtype(t, 0);
|
|
if(t != T)
|
|
expandmeth(t);
|
|
for(im=iface->type; im; im=im->down) {
|
|
imtype = methodfunc(im->type, 0);
|
|
tm = ifacelookdot(im->sym, t, &followptr, 0);
|
|
if(tm == T || tm->nointerface || !eqtype(methodfunc(tm->type, 0), imtype)) {
|
|
if(tm == T)
|
|
tm = ifacelookdot(im->sym, t, &followptr, 1);
|
|
*m = im;
|
|
*samename = tm;
|
|
*ptr = 0;
|
|
return 0;
|
|
}
|
|
// if pointer receiver in method,
|
|
// the method does not exist for value types.
|
|
rcvr = getthisx(tm->type)->type->type;
|
|
if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr && !isifacemethod(tm->type)) {
|
|
if(0 && debug['r'])
|
|
yyerror("interface pointer mismatch");
|
|
|
|
*m = im;
|
|
*samename = nil;
|
|
*ptr = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* even simpler simtype; get rid of ptr, bool.
|
|
* assuming that the front end has rejected
|
|
* all the invalid conversions (like ptr -> bool)
|
|
*/
|
|
int
|
|
simsimtype(Type *t)
|
|
{
|
|
int et;
|
|
|
|
if(t == 0)
|
|
return 0;
|
|
|
|
et = simtype[t->etype];
|
|
switch(et) {
|
|
case TPTR32:
|
|
et = TUINT32;
|
|
break;
|
|
case TPTR64:
|
|
et = TUINT64;
|
|
break;
|
|
case TBOOL:
|
|
et = TUINT8;
|
|
break;
|
|
}
|
|
return et;
|
|
}
|
|
|
|
NodeList*
|
|
concat(NodeList *a, NodeList *b)
|
|
{
|
|
if(a == nil)
|
|
return b;
|
|
if(b == nil)
|
|
return a;
|
|
|
|
a->end->next = b;
|
|
a->end = b->end;
|
|
b->end = nil;
|
|
return a;
|
|
}
|
|
|
|
NodeList*
|
|
list1(Node *n)
|
|
{
|
|
NodeList *l;
|
|
|
|
if(n == nil)
|
|
return nil;
|
|
if(n->op == OBLOCK && n->ninit == nil) {
|
|
// Flatten list and steal storage.
|
|
// Poison pointer to catch errant uses.
|
|
l = n->list;
|
|
n->list = (NodeList*)1;
|
|
return l;
|
|
}
|
|
l = mal(sizeof *l);
|
|
l->n = n;
|
|
l->end = l;
|
|
return l;
|
|
}
|
|
|
|
NodeList*
|
|
list(NodeList *l, Node *n)
|
|
{
|
|
return concat(l, list1(n));
|
|
}
|
|
|
|
void
|
|
listsort(NodeList** l, int(*f)(Node*, Node*))
|
|
{
|
|
NodeList *l1, *l2, *le;
|
|
|
|
if(*l == nil || (*l)->next == nil)
|
|
return;
|
|
|
|
l1 = *l;
|
|
l2 = *l;
|
|
for(;;) {
|
|
l2 = l2->next;
|
|
if(l2 == nil)
|
|
break;
|
|
l2 = l2->next;
|
|
if(l2 == nil)
|
|
break;
|
|
l1 = l1->next;
|
|
}
|
|
|
|
l2 = l1->next;
|
|
l1->next = nil;
|
|
l2->end = (*l)->end;
|
|
(*l)->end = l1;
|
|
|
|
l1 = *l;
|
|
listsort(&l1, f);
|
|
listsort(&l2, f);
|
|
|
|
if((*f)(l1->n, l2->n) < 0) {
|
|
*l = l1;
|
|
} else {
|
|
*l = l2;
|
|
l2 = l1;
|
|
l1 = *l;
|
|
}
|
|
|
|
// now l1 == *l; and l1 < l2
|
|
|
|
while ((l1 != nil) && (l2 != nil)) {
|
|
while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0)
|
|
l1 = l1->next;
|
|
|
|
// l1 is last one from l1 that is < l2
|
|
le = l1->next; // le is the rest of l1, first one that is >= l2
|
|
if(le != nil)
|
|
le->end = (*l)->end;
|
|
|
|
(*l)->end = l1; // cut *l at l1
|
|
*l = concat(*l, l2); // glue l2 to *l's tail
|
|
|
|
l1 = l2; // l1 is the first element of *l that is < the new l2
|
|
l2 = le; // ... because l2 now is the old tail of l1
|
|
}
|
|
|
|
*l = concat(*l, l2); // any remainder
|
|
}
|
|
|
|
NodeList*
|
|
listtreecopy(NodeList *l)
|
|
{
|
|
NodeList *out;
|
|
|
|
out = nil;
|
|
for(; l; l=l->next)
|
|
out = list(out, treecopy(l->n));
|
|
return out;
|
|
}
|
|
|
|
Node*
|
|
liststmt(NodeList *l)
|
|
{
|
|
Node *n;
|
|
|
|
n = nod(OBLOCK, N, N);
|
|
n->list = l;
|
|
if(l)
|
|
n->lineno = l->n->lineno;
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* return nelem of list
|
|
*/
|
|
int
|
|
count(NodeList *l)
|
|
{
|
|
vlong n;
|
|
|
|
n = 0;
|
|
for(; l; l=l->next)
|
|
n++;
|
|
if((int)n != n) { // Overflow.
|
|
yyerror("too many elements in list");
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* return nelem of list
|
|
*/
|
|
int
|
|
structcount(Type *t)
|
|
{
|
|
int v;
|
|
Iter s;
|
|
|
|
v = 0;
|
|
for(t = structfirst(&s, &t); t != T; t = structnext(&s))
|
|
v++;
|
|
return v;
|
|
}
|
|
|
|
/*
|
|
* return power of 2 of the constant
|
|
* operand. -1 if it is not a power of 2.
|
|
* 1000+ if it is a -(power of 2)
|
|
*/
|
|
int
|
|
powtwo(Node *n)
|
|
{
|
|
uvlong v, b;
|
|
int i;
|
|
|
|
if(n == N || n->op != OLITERAL || n->type == T)
|
|
goto no;
|
|
if(!isint[n->type->etype])
|
|
goto no;
|
|
|
|
v = mpgetfix(n->val.u.xval);
|
|
b = 1ULL;
|
|
for(i=0; i<64; i++) {
|
|
if(b == v)
|
|
return i;
|
|
b = b<<1;
|
|
}
|
|
|
|
if(!issigned[n->type->etype])
|
|
goto no;
|
|
|
|
v = -v;
|
|
b = 1ULL;
|
|
for(i=0; i<64; i++) {
|
|
if(b == v)
|
|
return i+1000;
|
|
b = b<<1;
|
|
}
|
|
|
|
no:
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* return the unsigned type for
|
|
* a signed integer type.
|
|
* returns T if input is not a
|
|
* signed integer type.
|
|
*/
|
|
Type*
|
|
tounsigned(Type *t)
|
|
{
|
|
|
|
// this is types[et+1], but not sure
|
|
// that this relation is immutable
|
|
switch(t->etype) {
|
|
default:
|
|
print("tounsigned: unknown type %T\n", t);
|
|
t = T;
|
|
break;
|
|
case TINT:
|
|
t = types[TUINT];
|
|
break;
|
|
case TINT8:
|
|
t = types[TUINT8];
|
|
break;
|
|
case TINT16:
|
|
t = types[TUINT16];
|
|
break;
|
|
case TINT32:
|
|
t = types[TUINT32];
|
|
break;
|
|
case TINT64:
|
|
t = types[TUINT64];
|
|
break;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
* magic number for signed division
|
|
* see hacker's delight chapter 10
|
|
*/
|
|
void
|
|
smagic(Magic *m)
|
|
{
|
|
int p;
|
|
uint64 ad, anc, delta, q1, r1, q2, r2, t;
|
|
uint64 mask, two31;
|
|
|
|
m->bad = 0;
|
|
switch(m->w) {
|
|
default:
|
|
m->bad = 1;
|
|
return;
|
|
case 8:
|
|
mask = 0xffLL;
|
|
break;
|
|
case 16:
|
|
mask = 0xffffLL;
|
|
break;
|
|
case 32:
|
|
mask = 0xffffffffLL;
|
|
break;
|
|
case 64:
|
|
mask = 0xffffffffffffffffLL;
|
|
break;
|
|
}
|
|
two31 = mask ^ (mask>>1);
|
|
|
|
p = m->w-1;
|
|
ad = m->sd;
|
|
if(m->sd < 0)
|
|
ad = -m->sd;
|
|
|
|
// bad denominators
|
|
if(ad == 0 || ad == 1 || ad == two31) {
|
|
m->bad = 1;
|
|
return;
|
|
}
|
|
|
|
t = two31;
|
|
ad &= mask;
|
|
|
|
anc = t - 1 - t%ad;
|
|
anc &= mask;
|
|
|
|
q1 = two31/anc;
|
|
r1 = two31 - q1*anc;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
|
|
q2 = two31/ad;
|
|
r2 = two31 - q2*ad;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
|
|
for(;;) {
|
|
p++;
|
|
q1 <<= 1;
|
|
r1 <<= 1;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
if(r1 >= anc) {
|
|
q1++;
|
|
r1 -= anc;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
}
|
|
|
|
q2 <<= 1;
|
|
r2 <<= 1;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
if(r2 >= ad) {
|
|
q2++;
|
|
r2 -= ad;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
}
|
|
|
|
delta = ad - r2;
|
|
delta &= mask;
|
|
if(q1 < delta || (q1 == delta && r1 == 0)) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
m->sm = q2+1;
|
|
if(m->sm & two31)
|
|
m->sm |= ~mask;
|
|
m->s = p-m->w;
|
|
}
|
|
|
|
/*
|
|
* magic number for unsigned division
|
|
* see hacker's delight chapter 10
|
|
*/
|
|
void
|
|
umagic(Magic *m)
|
|
{
|
|
int p;
|
|
uint64 nc, delta, q1, r1, q2, r2;
|
|
uint64 mask, two31;
|
|
|
|
m->bad = 0;
|
|
m->ua = 0;
|
|
|
|
switch(m->w) {
|
|
default:
|
|
m->bad = 1;
|
|
return;
|
|
case 8:
|
|
mask = 0xffLL;
|
|
break;
|
|
case 16:
|
|
mask = 0xffffLL;
|
|
break;
|
|
case 32:
|
|
mask = 0xffffffffLL;
|
|
break;
|
|
case 64:
|
|
mask = 0xffffffffffffffffLL;
|
|
break;
|
|
}
|
|
two31 = mask ^ (mask>>1);
|
|
|
|
m->ud &= mask;
|
|
if(m->ud == 0 || m->ud == two31) {
|
|
m->bad = 1;
|
|
return;
|
|
}
|
|
nc = mask - (-m->ud&mask)%m->ud;
|
|
p = m->w-1;
|
|
|
|
q1 = two31/nc;
|
|
r1 = two31 - q1*nc;
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
|
|
q2 = (two31-1) / m->ud;
|
|
r2 = (two31-1) - q2*m->ud;
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
|
|
for(;;) {
|
|
p++;
|
|
if(r1 >= nc-r1) {
|
|
q1 <<= 1;
|
|
q1++;
|
|
r1 <<= 1;
|
|
r1 -= nc;
|
|
} else {
|
|
q1 <<= 1;
|
|
r1 <<= 1;
|
|
}
|
|
q1 &= mask;
|
|
r1 &= mask;
|
|
if(r2+1 >= m->ud-r2) {
|
|
if(q2 >= two31-1) {
|
|
m->ua = 1;
|
|
}
|
|
q2 <<= 1;
|
|
q2++;
|
|
r2 <<= 1;
|
|
r2++;
|
|
r2 -= m->ud;
|
|
} else {
|
|
if(q2 >= two31) {
|
|
m->ua = 1;
|
|
}
|
|
q2 <<= 1;
|
|
r2 <<= 1;
|
|
r2++;
|
|
}
|
|
q2 &= mask;
|
|
r2 &= mask;
|
|
|
|
delta = m->ud - 1 - r2;
|
|
delta &= mask;
|
|
|
|
if(p < m->w+m->w)
|
|
if(q1 < delta || (q1 == delta && r1 == 0)) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
m->um = q2+1;
|
|
m->s = p-m->w;
|
|
}
|
|
|
|
Sym*
|
|
ngotype(Node *n)
|
|
{
|
|
if(n->type != T)
|
|
return typenamesym(n->type);
|
|
return S;
|
|
}
|
|
|
|
/*
|
|
* Convert raw string to the prefix that will be used in the symbol
|
|
* table. All control characters, space, '%' and '"', as well as
|
|
* non-7-bit clean bytes turn into %xx. The period needs escaping
|
|
* only in the last segment of the path, and it makes for happier
|
|
* users if we escape that as little as possible.
|
|
*
|
|
* If you edit this, edit ../ld/lib.c:/^pathtoprefix copy too.
|
|
*/
|
|
static char*
|
|
pathtoprefix(char *s)
|
|
{
|
|
static char hex[] = "0123456789abcdef";
|
|
char *p, *r, *w, *l;
|
|
int n;
|
|
|
|
// find first character past the last slash, if any.
|
|
l = s;
|
|
for(r=s; *r; r++)
|
|
if(*r == '/')
|
|
l = r+1;
|
|
|
|
// check for chars that need escaping
|
|
n = 0;
|
|
for(r=s; *r; r++)
|
|
if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
|
|
n++;
|
|
|
|
// quick exit
|
|
if(n == 0)
|
|
return s;
|
|
|
|
// escape
|
|
p = mal((r-s)+1+2*n);
|
|
for(r=s, w=p; *r; r++) {
|
|
if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
|
|
*w++ = '%';
|
|
*w++ = hex[(*r>>4)&0xF];
|
|
*w++ = hex[*r&0xF];
|
|
} else
|
|
*w++ = *r;
|
|
}
|
|
*w = '\0';
|
|
return p;
|
|
}
|
|
|
|
Pkg*
|
|
mkpkg(Strlit *path)
|
|
{
|
|
Pkg *p;
|
|
int h;
|
|
|
|
h = stringhash(path->s) & (nelem(phash)-1);
|
|
for(p=phash[h]; p; p=p->link)
|
|
if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
|
|
return p;
|
|
|
|
p = mal(sizeof *p);
|
|
p->path = path;
|
|
p->prefix = pathtoprefix(path->s);
|
|
p->link = phash[h];
|
|
phash[h] = p;
|
|
return p;
|
|
}
|
|
|
|
Strlit*
|
|
strlit(char *s)
|
|
{
|
|
Strlit *t;
|
|
|
|
t = mal(sizeof *t + strlen(s));
|
|
strcpy(t->s, s);
|
|
t->len = strlen(s);
|
|
return t;
|
|
}
|
|
|
|
void
|
|
addinit(Node **np, NodeList *init)
|
|
{
|
|
Node *n;
|
|
|
|
if(init == nil)
|
|
return;
|
|
|
|
n = *np;
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OLITERAL:
|
|
// There may be multiple refs to this node;
|
|
// introduce OCONVNOP to hold init list.
|
|
n = nod(OCONVNOP, n, N);
|
|
n->type = n->left->type;
|
|
n->typecheck = 1;
|
|
*np = n;
|
|
break;
|
|
}
|
|
n->ninit = concat(init, n->ninit);
|
|
n->ullman = UINF;
|
|
}
|
|
|
|
static char* reservedimports[] = {
|
|
"go",
|
|
"type",
|
|
};
|
|
|
|
int
|
|
isbadimport(Strlit *path)
|
|
{
|
|
int i;
|
|
char *s;
|
|
Rune r;
|
|
|
|
if(strlen(path->s) != path->len) {
|
|
yyerror("import path contains NUL");
|
|
return 1;
|
|
}
|
|
|
|
for(i=0; i<nelem(reservedimports); i++) {
|
|
if(strcmp(path->s, reservedimports[i]) == 0) {
|
|
yyerror("import path \"%s\" is reserved and cannot be used", path->s);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
s = path->s;
|
|
while(*s) {
|
|
s += chartorune(&r, s);
|
|
if(r == Runeerror) {
|
|
yyerror("import path contains invalid UTF-8 sequence: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(r < 0x20 || r == 0x7f) {
|
|
yyerror("import path contains control character: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(r == '\\') {
|
|
yyerror("import path contains backslash; use slash: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(isspacerune(r)) {
|
|
yyerror("import path contains space character: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}", r)) {
|
|
yyerror("import path contains invalid character '%C': \"%Z\"", r, path);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
checknotnil(Node *x, NodeList **init)
|
|
{
|
|
Node *n;
|
|
|
|
if(isinter(x->type)) {
|
|
x = nod(OITAB, x, N);
|
|
typecheck(&x, Erv);
|
|
}
|
|
n = nod(OCHECKNOTNIL, x, N);
|
|
n->typecheck = 1;
|
|
*init = list(*init, n);
|
|
}
|