diff options
Diffstat (limited to 'src/cmd/gc/lex.c')
-rw-r--r-- | src/cmd/gc/lex.c | 427 |
1 files changed, 352 insertions, 75 deletions
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 29b6d27ff..e71fd3848 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define EXTERN +#include <u.h> +#include <libc.h> #include "go.h" #include "y.tab.h" #include <ar.h> @@ -18,6 +19,7 @@ int yyprev; int yylast; static void lexinit(void); +static void lexinit1(void); static void lexfini(void); static void yytinit(void); static int getc(void); @@ -28,6 +30,61 @@ static void addidir(char*); static int getlinepragma(void); static char *goos, *goarch, *goroot; +// Compiler experiments. +// These are controlled by the GOEXPERIMENT environment +// variable recorded when the compiler is built. +static struct { + char *name; + int *val; +} exper[] = { +// {"rune32", &rune32}, + {nil, nil}, +}; + +static void +addexp(char *s) +{ + int i; + + for(i=0; exper[i].name != nil; i++) { + if(strcmp(exper[i].name, s) == 0) { + *exper[i].val = 1; + return; + } + } + + print("unknown experiment %s\n", s); + exits("unknown experiment"); +} + +static void +setexp(void) +{ + char *f[20]; + int i, nf; + + // The makefile #defines GOEXPERIMENT for us. + nf = getfields(GOEXPERIMENT, f, nelem(f), 1, ","); + for(i=0; i<nf; i++) + addexp(f[i]); +} + +char* +expstring(void) +{ + int i; + static char buf[512]; + + strcpy(buf, "X"); + for(i=0; exper[i].name != nil; i++) + if(*exper[i].val) + seprint(buf+strlen(buf), buf+sizeof buf, ",%s", exper[i].name); + if(strlen(buf) == 1) + strcpy(buf, "X,none"); + buf[1] = ':'; + return buf; +} + // Our own isdigit, isspace, isalpha, isalnum that take care // of EOF and other out of range arguments. static int @@ -64,7 +121,7 @@ yy_isalnum(int c) #define isalpha use_yy_isalpha_instead_of_isalpha #define isalnum use_yy_isalnum_instead_of_isalnum -#define DBG if(!debug['x']);else print +#define DBG if(!debug['x']){}else print enum { EOF = -1, @@ -75,24 +132,48 @@ usage(void) { print("gc: usage: %cg [flags] file.go...\n", thechar); print("flags:\n"); - // -A is allow use of "any" type, for bootstrapping + // -A allow use of "any" type, for bootstrapping + // -B disable bounds checking + // -E print imported declarations + // -K warn when lineno is zero + // -M print arguments to gmove + // -P print peephole diagnostics + // -R print optimizer diagnostics + // -g print code generation diagnostics + // -i print line history + // -j print variables to be initialized at runtime + // -r print generated helper functions + // -s print redundant types in composite literals + // -v print more information with -P or -R + // -y print declarations in cannedimports (used with -d) + // -% print non-static initializers + // -+ indicate that the runtime is being compiled + print(" -D PATH interpret local imports relative to this import path\n"); print(" -I DIR search for packages in DIR\n"); + print(" -L show full path in file:line prints\n"); + print(" -N disable optimizations\n"); + print(" -S print the assembly language\n"); + print(" -V print the compiler version\n"); + print(" -W print the parse tree after typing\n"); print(" -d print declarations\n"); print(" -e no limit on number of errors printed\n"); print(" -f print stack frame structure\n"); print(" -h panic on an error\n"); + print(" -l disable inlining\n"); + print(" -m print optimization decisions\n"); print(" -o file specify output file\n"); - print(" -S print the assembly language\n"); - print(" -V print the compiler version\n"); + print(" -p assumed import path for this code\n"); print(" -u disable package unsafe\n"); - print(" -w print the parse tree after typing\n"); + print(" -w print type checking details\n"); print(" -x print lex tokens\n"); - exit(0); + exits("usage"); } void fault(int s) { + USED(s); + // If we've already complained about things // in the program, don't bother complaining // about the seg fault too; let the user clean up @@ -106,11 +187,13 @@ int main(int argc, char *argv[]) { int i, c; - NodeList *l; + NodeList *l, *batch; char *p; - + +#ifdef SIGBUS signal(SIGBUS, fault); signal(SIGSEGV, fault); +#endif localpkg = mkpkg(strlit("")); localpkg->prefix = "\"\""; @@ -127,12 +210,18 @@ main(int argc, char *argv[]) typepkg = mkpkg(strlit("type")); typepkg->name = "type"; + weaktypepkg = mkpkg(strlit("weak.type")); + weaktypepkg->name = "weak.type"; + weaktypepkg->prefix = "weak.type"; // not weak%2etype + unsafepkg = mkpkg(strlit("unsafe")); unsafepkg->name = "unsafe"; goroot = getgoroot(); goos = getgoos(); goarch = thestring; + + setexp(); outfile = nil; ARGBEGIN { @@ -145,19 +234,37 @@ main(int argc, char *argv[]) case 'o': outfile = EARGF(usage()); break; - - case 'I': - addidir(EARGF(usage())); - break; + case 'p': + myimportpath = EARGF(usage()); + break; + case 'u': safemode = 1; break; + case 'D': + localimport = EARGF(usage()); + break; + + case 'I': + addidir(EARGF(usage())); + break; + case 'V': - print("%cg version %s\n", thechar, getgoversion()); - exit(0); + p = expstring(); + if(strcmp(p, "X:none") == 0) + p = ""; + print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p); + exits(0); } ARGEND + + // enable inlining. for now: + // default: inlining on. (debug['l'] == 1) + // -l: inlining off (debug['l'] == 0) + // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1) + if(debug['l'] <= 1) + debug['l'] = 1 - debug['l']; if(argc < 1) usage(); @@ -179,23 +286,14 @@ main(int argc, char *argv[]) *p = '/'; } - fmtinstall('O', Oconv); // node opcodes - fmtinstall('E', Econv); // etype opcodes - fmtinstall('J', Jconv); // all the node flags - fmtinstall('S', Sconv); // sym pointer - fmtinstall('T', Tconv); // type pointer - fmtinstall('N', Nconv); // node pointer - fmtinstall('Z', Zconv); // escaped string - fmtinstall('L', Lconv); // line number - fmtinstall('B', Bconv); // big numbers - fmtinstall('F', Fconv); // big float numbers - + fmtinstallgo(); betypeinit(); if(widthptr == 0) fatal("betypeinit failed"); lexinit(); typeinit(); + lexinit1(); yytinit(); blockgen = 1; @@ -236,24 +334,24 @@ main(int argc, char *argv[]) if(debug['f']) frame(1); - // Process top-level declarations in four phases. + // Process top-level declarations in phases. // Phase 1: const, type, and names and types of funcs. // This will gather all the information about types // and methods but doesn't depend on any of it. - // Phase 2: Variable assignments. - // To check interface assignments, depends on phase 1. - // Phase 3: Type check function bodies. - // Phase 4: Compile function bodies. defercheckwidth(); for(l=xtop; l; l=l->next) if(l->n->op != ODCL && l->n->op != OAS) typecheck(&l->n, Etop); + + // Phase 2: Variable assignments. + // To check interface assignments, depends on phase 1. for(l=xtop; l; l=l->next) if(l->n->op == ODCL || l->n->op == OAS) typecheck(&l->n, Etop); resumetypecopy(); resumecheckwidth(); + // Phase 3: Type check function bodies. for(l=xtop; l; l=l->next) { if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) { curfn = l->n; @@ -269,6 +367,37 @@ main(int argc, char *argv[]) if(nsavederrors+nerrors) errorexit(); + // Phase 4: Inlining + if (debug['l'] > 1) { + // Typecheck imported function bodies if debug['l'] > 1, + // otherwise lazily when used or re-exported. + for(l=importlist; l; l=l->next) + if (l->n->inl) { + saveerrors(); + typecheckinl(l->n); + } + + if(nsavederrors+nerrors) + errorexit(); + } + + if (debug['l']) { + // Find functions that can be inlined and clone them before walk expands them. + for(l=xtop; l; l=l->next) + if(l->n->op == ODCLFUNC) + caninl(l->n); + + // Expand inlineable calls in all functions + for(l=xtop; l; l=l->next) + if(l->n->op == ODCLFUNC) + inlcalls(l->n); + } + + // Phase 5: escape analysis. + if(!debug['N']) + escapes(xtop); + + // Phase 6: Compile top level functions. for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) funccompile(l->n, 0); @@ -276,14 +405,21 @@ main(int argc, char *argv[]) if(nsavederrors+nerrors == 0) fninit(xtop); + // Phase 6b: Compile all closures. + // Can generate more closures, so run in batches. while(closures) { - l = closures; + batch = closures; closures = nil; - for(; l; l=l->next) { + if(debug['l']) + for(l=batch; l; l=l->next) + inlcalls(l->n); + if(!debug['N']) + escapes(batch); + for(l=batch; l; l=l->next) funccompile(l->n, 1); - } } + // Phase 7: check external declarations. for(l=externdcl; l; l=l->next) if(l->n->op == ONAME) typecheck(&l->n, Erv); @@ -297,7 +433,7 @@ main(int argc, char *argv[]) errorexit(); flusherrors(); - exit(0); + exits(0); return 0; } @@ -308,18 +444,30 @@ saveerrors(void) nerrors = 0; } +/* + * macro to portably read/write archive header. + * 'cmd' is read/write/Bread/Bwrite, etc. + */ +#define HEADER_IO(cmd, f, h) cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\ + || cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\ + || cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\ + || cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\ + || cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\ + || cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\ + || cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag) + static int arsize(Biobuf *b, char *name) { - struct ar_hdr *a; + struct ar_hdr a; - if((a = Brdline(b, '\n')) == nil) + if (HEADER_IO(Bread, b, a)) return -1; - if(Blinelen(b) != sizeof(struct ar_hdr)) - return -1; - if(strncmp(a->name, name, strlen(name)) != 0) + + if(strncmp(a.name, name, strlen(name)) != 0) return -1; - return atoi(a->size); + + return atoi(a.size); } static int @@ -366,15 +514,19 @@ addidir(char* dir) static int islocalname(Strlit *name) { - if(!windows && name->len >= 1 && name->s[0] == '/') + if(name->len >= 1 && name->s[0] == '/') return 1; if(windows && name->len >= 3 && yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') return 1; if(name->len >= 2 && strncmp(name->s, "./", 2) == 0) return 1; + if(name->len == 1 && strncmp(name->s, ".", 1) == 0) + return 1; if(name->len >= 3 && strncmp(name->s, "../", 3) == 0) return 1; + if(name->len == 2 && strncmp(name->s, "..", 2) == 0) + return 1; return 0; } @@ -400,8 +552,8 @@ findpkg(Strlit *name) } // local imports should be canonicalized already. - // don't want to see "container/../container/vector" - // as different from "container/vector". + // don't want to see "encoding/../encoding/base64" + // as different from "encoding/base64". q = mal(name->len+1); memmove(q, name->s, name->len); q[name->len] = '\0'; @@ -430,6 +582,13 @@ findpkg(Strlit *name) return 0; } +static void +fakeimport(void) +{ + importpkg = mkpkg(strlit("fake")); + cannedimports("fake.6", "$$\n"); +} + void importfile(Val *f, int line) { @@ -438,18 +597,27 @@ importfile(Val *f, int line) int32 c; int len; Strlit *path; - char *cleanbuf; + char *cleanbuf, *prefix; + + USED(line); // TODO(rsc): don't bother reloading imports more than once? if(f->ctype != CTSTR) { yyerror("import statement not a string"); + fakeimport(); return; } - if(strlen(f->u.sval->s) != f->u.sval->len) { - yyerror("import path contains NUL"); - errorexit(); + if(f->u.sval->len == 0) { + yyerror("import path is empty"); + fakeimport(); + return; + } + + if(isbadimport(f->u.sval)) { + fakeimport(); + return; } // The package name main is no longer reserved, @@ -461,6 +629,11 @@ importfile(Val *f, int line) errorexit(); } + if(myimportpath != nil && strcmp(f->u.sval->s, myimportpath) == 0) { + yyerror("import \"%Z\" while compiling that package (import cycle)", f->u.sval); + errorexit(); + } + if(strcmp(f->u.sval->s, "unsafe") == 0) { if(safemode) { yyerror("cannot import package unsafe"); @@ -473,8 +646,16 @@ importfile(Val *f, int line) path = f->u.sval; if(islocalname(path)) { - cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); - strcpy(cleanbuf, pathname); + if(path->s[0] == '/') { + yyerror("import path cannot be absolute path"); + fakeimport(); + return; + } + prefix = pathname; + if(localimport != nil) + prefix = localimport; + cleanbuf = mal(strlen(prefix) + strlen(path->s) + 2); + strcpy(cleanbuf, prefix); strcat(cleanbuf, "/"); strcat(cleanbuf, path->s); cleanname(cleanbuf); @@ -482,14 +663,14 @@ importfile(Val *f, int line) } if(!findpkg(path)) { - yyerror("can't find import: %Z", f->u.sval); + yyerror("can't find import: \"%Z\"", f->u.sval); errorexit(); } importpkg = mkpkg(path); imp = Bopen(namebuf, OREAD); if(imp == nil) { - yyerror("can't open import: %Z: %r", f->u.sval); + yyerror("can't open import: \"%Z\": %r", f->u.sval); errorexit(); } file = strdup(namebuf); @@ -509,7 +690,7 @@ importfile(Val *f, int line) yyerror("import %s: not a go object file", file); errorexit(); } - q = smprint("%s %s %s", getgoos(), thestring, getgoversion()); + q = smprint("%s %s %s %s", getgoos(), thestring, getgoversion(), expstring()); if(strcmp(p+10, q) != 0) { yyerror("import %s: object is [%s] expected [%s]", file, p+10, q); errorexit(); @@ -546,7 +727,7 @@ importfile(Val *f, int line) continue; return; } - yyerror("no import in: %Z", f->u.sval); + yyerror("no import in \"%Z\"", f->u.sval); unimportfile(); } @@ -665,7 +846,6 @@ l0: ep = lexbuf+sizeof lexbuf; *cp++ = c; c = c1; - c1 = 0; goto casedot; } if(c1 == '.') { @@ -717,6 +897,8 @@ l0: ncp += ncp; } c = getr(); + if(c == '\r') + continue; if(c == EOF) { yyerror("eof in string"); break; @@ -750,7 +932,7 @@ l0: } yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval)); mpmovecfix(yylval.val.u.xval, v); - yylval.val.ctype = CTINT; + yylval.val.ctype = CTRUNE; DBG("lex: codepoint literal\n"); strcpy(litbuf, "string literal"); return LLITERAL; @@ -1008,7 +1190,7 @@ lx: return c; asop: - yylval.lint = c; // rathole to hold which asop + yylval.i = c; // rathole to hold which asop DBG("lex: TOKEN ASOP %c\n", c); return LASOP; @@ -1056,7 +1238,6 @@ talph: return s->lexical; tnum: - c1 = 0; cp = lexbuf; ep = lexbuf+sizeof lexbuf; if(c != '0') { @@ -1247,7 +1428,7 @@ static int getlinepragma(void) { int i, c, n; - char *cp, *ep; + char *cp, *ep, *linep; Hist *h; for(i=0; i<5; i++) { @@ -1258,32 +1439,36 @@ getlinepragma(void) cp = lexbuf; ep = lexbuf+sizeof(lexbuf)-5; + linep = nil; for(;;) { c = getr(); - if(c == '\n' || c == EOF) + if(c == EOF) goto out; + if(c == '\n') + break; if(c == ' ') continue; if(c == ':') - break; + linep = cp; if(cp < ep) *cp++ = c; } *cp = 0; + if(linep == nil || linep >= ep) + goto out; + *linep++ = '\0'; n = 0; - for(;;) { - c = getr(); - if(!yy_isdigit(c)) - break; - n = n*10 + (c-'0'); + for(cp=linep; *cp; cp++) { + if(*cp < '0' || *cp > '9') + goto out; + n = n*10 + *cp - '0'; if(n > 1e8) { yyerror("line number out of range"); errorexit(); } } - - if(c != '\n' || n <= 0) + if(n <= 0) goto out; // try to avoid allocating file name over and over @@ -1334,7 +1519,7 @@ yylex(void) // Track last two tokens returned by yylex. yyprev = yylast; yylast = lx; - return lx; + return lx; } static int @@ -1561,7 +1746,6 @@ static struct "complex128", LNAME, TCOMPLEX128, OXXX, "bool", LNAME, TBOOL, OXXX, - "byte", LNAME, TUINT8, OXXX, "string", LNAME, TSTRING, OXXX, "any", LNAME, TANY, OXXX, @@ -1592,11 +1776,12 @@ static struct "type", LTYPE, Txxx, OXXX, "var", LVAR, Txxx, OXXX, - "append", LNAME, Txxx, OAPPEND, + "append", LNAME, Txxx, OAPPEND, "cap", LNAME, Txxx, OCAP, "close", LNAME, Txxx, OCLOSE, "complex", LNAME, Txxx, OCOMPLEX, "copy", LNAME, Txxx, OCOPY, + "delete", LNAME, Txxx, ODELETE, "imag", LNAME, Txxx, OIMAG, "len", LNAME, Txxx, OLEN, "make", LNAME, Txxx, OMAKE, @@ -1621,6 +1806,7 @@ lexinit(void) Sym *s, *s1; Type *t; int etype; + Val v; /* * initialize basic types array @@ -1649,6 +1835,16 @@ lexinit(void) s1->def = typenod(t); continue; } + + etype = syms[i].op; + if(etype != OXXX) { + s1 = pkglookup(syms[i].name, builtinpkg); + s1->lexical = LNAME; + s1->def = nod(ONAME, N, N); + s1->def->sym = s1; + s1->def->etype = etype; + s1->def->builtin = 1; + } } // logically, the type of a string literal. @@ -1676,6 +1872,77 @@ lexinit(void) types[TBLANK] = typ(TBLANK); s->def->type = types[TBLANK]; nblank = s->def; + + s = pkglookup("_", builtinpkg); + s->block = -100; + s->def = nod(ONAME, N, N); + s->def->sym = s; + types[TBLANK] = typ(TBLANK); + s->def->type = types[TBLANK]; + + types[TNIL] = typ(TNIL); + s = pkglookup("nil", builtinpkg); + v.ctype = CTNIL; + s->def = nodlit(v); + s->def->sym = s; +} + +static void +lexinit1(void) +{ + Sym *s, *s1; + Type *t, *f, *rcvr, *in, *out; + + // t = interface { Error() string } + rcvr = typ(TSTRUCT); + rcvr->type = typ(TFIELD); + rcvr->type->type = ptrto(typ(TSTRUCT)); + rcvr->funarg = 1; + in = typ(TSTRUCT); + in->funarg = 1; + out = typ(TSTRUCT); + out->type = typ(TFIELD); + out->type->type = types[TSTRING]; + out->funarg = 1; + f = typ(TFUNC); + *getthis(f) = rcvr; + *getoutarg(f) = out; + *getinarg(f) = in; + f->thistuple = 1; + f->intuple = 0; + f->outnamed = 0; + f->outtuple = 1; + t = typ(TINTER); + t->type = typ(TFIELD); + t->type->sym = lookup("Error"); + t->type->type = f; + + // error type + s = lookup("error"); + s->lexical = LNAME; + errortype = t; + errortype->sym = s; + s1 = pkglookup("error", builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(errortype); + + // byte alias + s = lookup("byte"); + s->lexical = LNAME; + bytetype = typ(TUINT8); + bytetype->sym = s; + s1 = pkglookup("byte", builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(bytetype); + + // rune alias + s = lookup("rune"); + s->lexical = LNAME; + runetype = typ(TINT32); + runetype->sym = s; + s1 = pkglookup("rune", builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(runetype); } static void @@ -1713,7 +1980,18 @@ lexfini(void) // there's only so much table-driven we can handle. // these are special cases. - types[TNIL] = typ(TNIL); + s = lookup("byte"); + if(s->def == N) + s->def = typenod(bytetype); + + s = lookup("error"); + if(s->def == N) + s->def = typenod(errortype); + + s = lookup("rune"); + if(s->def == N) + s->def = typenod(runetype); + s = lookup("nil"); if(s->def == N) { v.ctype = CTNIL; @@ -1740,7 +2018,6 @@ lexfini(void) } nodfp = nod(ONAME, N, N); - nodfp->noescape = 1; nodfp->type = types[TINT32]; nodfp->xoffset = 0; nodfp->class = PPARAM; @@ -1923,7 +2200,7 @@ mkpackage(char* pkgname) // errors if a conflicting top-level name is // introduced by a different file. if(!s->def->used && !nsyntaxerrors) - yyerrorl(s->def->lineno, "imported and not used: %Z", s->def->pkg->path); + yyerrorl(s->def->lineno, "imported and not used: \"%Z\"", s->def->pkg->path); s->def = N; continue; } @@ -1931,7 +2208,7 @@ mkpackage(char* pkgname) // throw away top-level name left over // from previous import . "x" if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) { - yyerrorl(s->def->pack->lineno, "imported and not used: %Z", s->def->pack->pkg->path); + yyerrorl(s->def->pack->lineno, "imported and not used: \"%Z\"", s->def->pack->pkg->path); s->def->pack->used = 1; } s->def = N; |