1
0
mirror of https://github.com/golang/go synced 2024-10-04 20:11:22 -06:00
go/src/cmd/gc/fmt.c
Russ Cox 25922c0658 cmd/gc: introduce hidden closure functions earlier
The original implementation of closures created the
underlying top-level function during walk, which is fairly
late in the compilation process and caused ordering-based
complications due to earlier stages that had to be repeated
any number of times.

Create the underlying function during typecheck, much
earlier, so that later stages can be run just once.

The result is a simpler compilation sequence.

R=ken2
CC=golang-dev
https://golang.org/cl/6279049
2012-06-04 17:07:59 -04:00

1644 lines
34 KiB
C

// Copyright 2011 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 "opnames.h"
//
// Format conversions
// %L int Line numbers
//
// %E int etype values (aka 'Kind')
//
// %O int Node Opcodes
// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
//
// %J Node* Node details
// Flags: "%hJ" supresses things not relevant until walk.
//
// %V Val* Constant values
//
// %S Sym* Symbols
// Flags: +,- #: mode (see below)
// "%hS" unqualified identifier in any mode
// "%hhS" in export mode: unqualified identifier if exported, qualified if not
//
// %T Type* Types
// Flags: +,- #: mode (see below)
// 'l' definition instead of name.
// 'h' omit "func" and receiver in function types
// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
//
// %N Node* Nodes
// Flags: +,- #: mode (see below)
// 'h' (only in +/debug mode) suppress recursion
// 'l' (only in Error mode) print "foo (type Bar)"
//
// %H NodeList* NodeLists
// Flags: those of %N
// ',' separate items with ',' instead of ';'
//
// %Z Strlit* String literals
//
// In mparith1.c:
// %B Mpint* Big integers
// %F Mpflt* Big floats
//
// %S, %T and %N obey use the following flags to set the format mode:
enum {
FErr, // error mode (default)
FDbg, // "%+N" debug mode
FExp, // "%#N" export mode
FTypeId, // "%-N" turning-types-into-symbols-mode: identical types give identical strings
};
static int fmtmode;
static int fmtpkgpfx; // %uT stickyness
//
// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode.
//
// The mode flags +, - and # are sticky, meaning they persist through
// recursions of %N, %T and %S, but not the h and l flags. The u flag is
// sticky only on %T recursions and only used in %-/Sym mode.
//
// Useful format combinations:
//
// %+N %+H multiline recursive debug dump of node/nodelist
// %+hN %+hH non recursive debug dump
//
// %#N %#T export format
// %#lT type definition instead of name
// %#hT omit"func" and receiver in function signature
//
// %lN "foo (type Bar)" for error messages
//
// %-T type identifiers
// %-hT type identifiers without "func" and arg names in type signatures (methodsym)
// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
//
static int
setfmode(unsigned long *flags)
{
int fm;
fm = fmtmode;
if(*flags & FmtSign)
fmtmode = FDbg;
else if(*flags & FmtSharp)
fmtmode = FExp;
else if(*flags & FmtLeft)
fmtmode = FTypeId;
*flags &= ~(FmtSharp|FmtLeft|FmtSign);
return fm;
}
// Fmt "%L": Linenumbers
static int
Lconv(Fmt *fp)
{
struct
{
Hist* incl; /* start of this include file */
int32 idel; /* delta line number to apply to include */
Hist* line; /* start of this #line directive */
int32 ldel; /* delta line number to apply to #line */
} a[HISTSZ];
int32 lno, d;
int i, n;
Hist *h;
lno = va_arg(fp->args, int32);
n = 0;
for(h=hist; h!=H; h=h->link) {
if(h->offset < 0)
continue;
if(lno < h->line)
break;
if(h->name) {
if(h->offset > 0) {
// #line directive
if(n > 0 && n < HISTSZ) {
a[n-1].line = h;
a[n-1].ldel = h->line - h->offset + 1;
}
} else {
// beginning of file
if(n < HISTSZ) {
a[n].incl = h;
a[n].idel = h->line;
a[n].line = 0;
}
n++;
}
continue;
}
n--;
if(n > 0 && n < HISTSZ) {
d = h->line - a[n].incl->line;
a[n-1].ldel += d;
a[n-1].idel += d;
}
}
if(n > HISTSZ)
n = HISTSZ;
for(i=n-1; i>=0; i--) {
if(i != n-1) {
if(fp->flags & ~(FmtWidth|FmtPrec))
break;
fmtprint(fp, " ");
}
if(debug['L'] || (fp->flags&FmtLong))
fmtprint(fp, "%s/", pathname);
if(a[i].line)
fmtprint(fp, "%s:%d[%s:%d]",
a[i].line->name, lno-a[i].ldel+1,
a[i].incl->name, lno-a[i].idel+1);
else
fmtprint(fp, "%s:%d",
a[i].incl->name, lno-a[i].idel+1);
lno = a[i].incl->line - 1; // now print out start of this file
}
if(n == 0)
fmtprint(fp, "<epoch>");
return 0;
}
static char*
goopnames[] =
{
[OADDR] = "&",
[OADD] = "+",
[OADDSTR] = "+",
[OANDAND] = "&&",
[OANDNOT] = "&^",
[OAND] = "&",
[OAPPEND] = "append",
[OAS] = "=",
[OAS2] = "=",
[OBREAK] = "break",
[OCALL] = "function call", // not actual syntax
[OCAP] = "cap",
[OCASE] = "case",
[OCLOSE] = "close",
[OCOMPLEX] = "complex",
[OCOM] = "^",
[OCONTINUE] = "continue",
[OCOPY] = "copy",
[ODEC] = "--",
[ODELETE] = "delete",
[ODEFER] = "defer",
[ODIV] = "/",
[OEQ] = "==",
[OFALL] = "fallthrough",
[OFOR] = "for",
[OGE] = ">=",
[OGOTO] = "goto",
[OGT] = ">",
[OIF] = "if",
[OIMAG] = "imag",
[OINC] = "++",
[OIND] = "*",
[OLEN] = "len",
[OLE] = "<=",
[OLSH] = "<<",
[OLT] = "<",
[OMAKE] = "make",
[OMINUS] = "-",
[OMOD] = "%",
[OMUL] = "*",
[ONEW] = "new",
[ONE] = "!=",
[ONOT] = "!",
[OOROR] = "||",
[OOR] = "|",
[OPANIC] = "panic",
[OPLUS] = "+",
[OPRINTN] = "println",
[OPRINT] = "print",
[ORANGE] = "range",
[OREAL] = "real",
[ORECV] = "<-",
[ORETURN] = "return",
[ORSH] = ">>",
[OSELECT] = "select",
[OSEND] = "<-",
[OSUB] = "-",
[OSWITCH] = "switch",
[OXOR] = "^",
};
// Fmt "%O": Node opcodes
static int
Oconv(Fmt *fp)
{
int o;
o = va_arg(fp->args, int);
if((fp->flags & FmtSharp) || fmtmode != FDbg)
if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
return fmtstrcpy(fp, goopnames[o]);
if(o >= 0 && o < nelem(opnames) && opnames[o] != nil)
return fmtstrcpy(fp, opnames[o]);
return fmtprint(fp, "O-%d", o);
}
static const char* classnames[] = {
"Pxxx",
"PEXTERN",
"PAUTO",
"PPARAM",
"PPARAMOUT",
"PPARAMREF",
"PFUNC",
};
// Fmt "%J": Node details.
static int
Jconv(Fmt *fp)
{
Node *n;
char *s;
int c;
n = va_arg(fp->args, Node*);
c = fp->flags&FmtShort;
if(!c && n->ullman != 0)
fmtprint(fp, " u(%d)", n->ullman);
if(!c && n->addable != 0)
fmtprint(fp, " a(%d)", n->addable);
if(!c && n->vargen != 0)
fmtprint(fp, " g(%d)", n->vargen);
if(n->lineno != 0)
fmtprint(fp, " l(%d)", n->lineno);
if(!c && n->xoffset != BADWIDTH)
fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
if(n->class != 0) {
s = "";
if(n->class & PHEAP) s = ",heap";
if((n->class & ~PHEAP) < nelem(classnames))
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
else
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
}
if(n->colas != 0)
fmtprint(fp, " colas(%d)", n->colas);
if(n->funcdepth != 0)
fmtprint(fp, " f(%d)", n->funcdepth);
switch(n->esc) {
case EscUnknown:
break;
case EscHeap:
fmtprint(fp, " esc(h)");
break;
case EscScope:
fmtprint(fp, " esc(s)");
break;
case EscNone:
fmtprint(fp, " esc(no)");
break;
case EscNever:
if(!c)
fmtprint(fp, " esc(N)");
break;
default:
fmtprint(fp, " esc(%d)", n->esc);
break;
}
if(n->escloopdepth)
fmtprint(fp, " ld(%d)", n->escloopdepth);
if(!c && n->typecheck != 0)
fmtprint(fp, " tc(%d)", n->typecheck);
if(!c && n->dodata != 0)
fmtprint(fp, " dd(%d)", n->dodata);
if(n->isddd != 0)
fmtprint(fp, " isddd(%d)", n->isddd);
if(n->implicit != 0)
fmtprint(fp, " implicit(%d)", n->implicit);
if(n->embedded != 0)
fmtprint(fp, " embedded(%d)", n->embedded);
if(!c && n->used != 0)
fmtprint(fp, " used(%d)", n->used);
return 0;
}
// Fmt "%V": Values
static int
Vconv(Fmt *fp)
{
Val *v;
vlong x;
v = va_arg(fp->args, Val*);
switch(v->ctype) {
case CTINT:
if((fp->flags & FmtSharp) || fmtmode == FExp)
return fmtprint(fp, "%#B", v->u.xval);
return fmtprint(fp, "%B", v->u.xval);
case CTRUNE:
x = mpgetfix(v->u.xval);
if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
return fmtprint(fp, "'%c'", (int)x);
if(0 <= x && x < (1<<16))
return fmtprint(fp, "'\\u%04ux'", (int)x);
if(0 <= x && x <= Runemax)
return fmtprint(fp, "'\\U%08llux'", x);
return fmtprint(fp, "('\\x00' + %B)", v->u.xval);
case CTFLT:
if((fp->flags & FmtSharp) || fmtmode == FExp)
return fmtprint(fp, "%F", v->u.fval);
return fmtprint(fp, "%#F", v->u.fval);
case CTCPLX:
if((fp->flags & FmtSharp) || fmtmode == FExp)
return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag);
return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag);
case CTSTR:
return fmtprint(fp, "\"%Z\"", v->u.sval);
case CTBOOL:
if( v->u.bval)
return fmtstrcpy(fp, "true");
return fmtstrcpy(fp, "false");
case CTNIL:
return fmtstrcpy(fp, "nil");
}
return fmtprint(fp, "<%d>", v->ctype);
}
// Fmt "%Z": escaped string literals
static int
Zconv(Fmt *fp)
{
Rune r;
Strlit *sp;
char *s, *se;
int n;
sp = va_arg(fp->args, Strlit*);
if(sp == nil)
return fmtstrcpy(fp, "<nil>");
s = sp->s;
se = s + sp->len;
while(s < se) {
n = chartorune(&r, s);
s += n;
switch(r) {
case Runeerror:
if(n == 1) {
fmtprint(fp, "\\x%02x", (uchar)*(s-1));
break;
}
// fall through
default:
if(r < ' ') {
fmtprint(fp, "\\x%02x", r);
break;
}
fmtrune(fp, r);
break;
case '\t':
fmtstrcpy(fp, "\\t");
break;
case '\n':
fmtstrcpy(fp, "\\n");
break;
case '\"':
case '\\':
fmtrune(fp, '\\');
fmtrune(fp, r);
break;
}
}
return 0;
}
/*
s%,%,\n%g
s%\n+%\n%g
s%^[ ]*T%%g
s%,.*%%g
s%.+% [T&] = "&",%g
s%^ ........*\]%&~%g
s%~ %%g
*/
static char*
etnames[] =
{
[TINT] = "INT",
[TUINT] = "UINT",
[TINT8] = "INT8",
[TUINT8] = "UINT8",
[TINT16] = "INT16",
[TUINT16] = "UINT16",
[TINT32] = "INT32",
[TUINT32] = "UINT32",
[TINT64] = "INT64",
[TUINT64] = "UINT64",
[TUINTPTR] = "UINTPTR",
[TFLOAT32] = "FLOAT32",
[TFLOAT64] = "FLOAT64",
[TCOMPLEX64] = "COMPLEX64",
[TCOMPLEX128] = "COMPLEX128",
[TBOOL] = "BOOL",
[TPTR32] = "PTR32",
[TPTR64] = "PTR64",
[TFUNC] = "FUNC",
[TARRAY] = "ARRAY",
[TSTRUCT] = "STRUCT",
[TCHAN] = "CHAN",
[TMAP] = "MAP",
[TINTER] = "INTER",
[TFORW] = "FORW",
[TFIELD] = "FIELD",
[TSTRING] = "STRING",
[TANY] = "ANY",
};
// Fmt "%E": etype
static int
Econv(Fmt *fp)
{
int et;
et = va_arg(fp->args, int);
if(et >= 0 && et < nelem(etnames) && etnames[et] != nil)
return fmtstrcpy(fp, etnames[et]);
return fmtprint(fp, "E-%d", et);
}
// Fmt "%S": syms
static int
symfmt(Fmt *fp, Sym *s)
{
char *p;
if(s->pkg && !(fp->flags&FmtShort)) {
switch(fmtmode) {
case FErr: // This is for the user
if(s->pkg == localpkg)
return fmtstrcpy(fp, s->name);
// If the name was used by multiple packages, display the full path,
if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1)
return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
case FDbg:
return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
case FTypeId:
if(fp->flags&FmtUnsigned)
return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash
return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym
case FExp:
if(s->pkg != builtinpkg)
return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name);
}
}
if(fp->flags&FmtByte) { // FmtByte (hh) implies FmtShort (h)
// skip leading "type." in method name
p = utfrrune(s->name, '.');
if(p)
p++;
else
p = s->name;
// exportname needs to see the name without the prefix too.
if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg)
return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p);
return fmtstrcpy(fp, p);
}
return fmtstrcpy(fp, s->name);
}
static char*
basicnames[] =
{
[TINT] = "int",
[TUINT] = "uint",
[TINT8] = "int8",
[TUINT8] = "uint8",
[TINT16] = "int16",
[TUINT16] = "uint16",
[TINT32] = "int32",
[TUINT32] = "uint32",
[TINT64] = "int64",
[TUINT64] = "uint64",
[TUINTPTR] = "uintptr",
[TFLOAT32] = "float32",
[TFLOAT64] = "float64",
[TCOMPLEX64] = "complex64",
[TCOMPLEX128] = "complex128",
[TBOOL] = "bool",
[TANY] = "any",
[TSTRING] = "string",
[TNIL] = "nil",
[TIDEAL] = "ideal",
[TBLANK] = "blank",
};
static int
typefmt(Fmt *fp, Type *t)
{
Type *t1;
Sym *s;
if(t == T)
return fmtstrcpy(fp, "<T>");
if (t == bytetype || t == runetype) {
// in %-T mode collapse rune and byte with their originals.
if(fmtmode != FTypeId)
return fmtprint(fp, "%hS", t->sym);
t = types[t->etype];
}
if(t == errortype)
return fmtstrcpy(fp, "error");
// Unless the 'l' flag was specified, if the type has a name, just print that name.
if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
switch(fmtmode) {
case FTypeId:
if(fp->flags&FmtShort)
return fmtprint(fp, "%hS", t->sym);
if(fp->flags&FmtUnsigned)
return fmtprint(fp, "%uS", t->sym);
// fallthrough
case FExp:
if(t->sym->pkg == localpkg && t->vargen)
return fmtprint(fp, "%S·%d", t->sym, t->vargen);
break;
}
return fmtprint(fp, "%S", t->sym);
}
if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
if(fmtmode == FErr && (t == idealbool || t == idealstring))
fmtstrcpy(fp, "ideal ");
return fmtstrcpy(fp, basicnames[t->etype]);
}
if(fmtmode == FDbg)
fmtprint(fp, "%E-", t->etype);
switch(t->etype) {
case TPTR32:
case TPTR64:
if(fmtmode == FTypeId && (fp->flags&FmtShort))
return fmtprint(fp, "*%hT", t->type);
return fmtprint(fp, "*%T", t->type);
case TARRAY:
if(t->bound >= 0)
return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
if(t->bound == -100)
return fmtprint(fp, "[...]%T", t->type);
return fmtprint(fp, "[]%T", t->type);
case TCHAN:
switch(t->chan) {
case Crecv:
return fmtprint(fp, "<-chan %T", t->type);
case Csend:
return fmtprint(fp, "chan<- %T", t->type);
}
if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
return fmtprint(fp, "chan (%T)", t->type);
return fmtprint(fp, "chan %T", t->type);
case TMAP:
return fmtprint(fp, "map[%T]%T", t->down, t->type);
case TINTER:
fmtstrcpy(fp, "interface {");
for(t1=t->type; t1!=T; t1=t1->down)
if(exportname(t1->sym->name)) {
if(t1->down)
fmtprint(fp, " %hS%hT;", t1->sym, t1->type);
else
fmtprint(fp, " %hS%hT ", t1->sym, t1->type);
} else {
// non-exported method names must be qualified
if(t1->down)
fmtprint(fp, " %uS%hT;", t1->sym, t1->type);
else
fmtprint(fp, " %uS%hT ", t1->sym, t1->type);
}
fmtstrcpy(fp, "}");
return 0;
case TFUNC:
if(fp->flags & FmtShort) {
fmtprint(fp, "%T", getinargx(t));
} else {
if(t->thistuple)
fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t));
else
fmtprint(fp, "func%T", getinargx(t));
}
switch(t->outtuple) {
case 0:
break;
case 1:
if(fmtmode != FExp) {
fmtprint(fp, " %T", getoutargx(t)->type->type); // struct->field->field's type
break;
}
default:
fmtprint(fp, " %T", getoutargx(t));
break;
}
return 0;
case TSTRUCT:
if(t->funarg) {
fmtstrcpy(fp, "(");
if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags
for(t1=t->type; t1!=T; t1=t1->down)
if(t1->down)
fmtprint(fp, "%hT, ", t1);
else
fmtprint(fp, "%hT", t1);
} else {
for(t1=t->type; t1!=T; t1=t1->down)
if(t1->down)
fmtprint(fp, "%T, ", t1);
else
fmtprint(fp, "%T", t1);
}
fmtstrcpy(fp, ")");
} else {
fmtstrcpy(fp, "struct {");
for(t1=t->type; t1!=T; t1=t1->down)
if(t1->down)
fmtprint(fp, " %lT;", t1);
else
fmtprint(fp, " %lT ", t1);
fmtstrcpy(fp, "}");
}
return 0;
case TFIELD:
if(!(fp->flags&FmtShort)) {
s = t->sym;
// Take the name from the original, lest we substituted it with .anon%d
if (t->nname && (fmtmode == FErr || fmtmode == FExp))
s = t->nname->orig->sym;
if(s != S && !t->embedded) {
if(fp->flags&FmtLong)
fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg)
else
fmtprint(fp, "%S ", s);
} else if(fmtmode == FExp) {
// TODO(rsc) this breaks on the eliding of unused arguments in the backend
// when this is fixed, the special case in dcl.c checkarglist can go.
//if(t->funarg)
// fmtstrcpy(fp, "_ ");
//else
fmtstrcpy(fp, "? ");
}
}
if(t->isddd)
fmtprint(fp, "...%T", t->type->type);
else
fmtprint(fp, "%T", t->type);
if(!(fp->flags&FmtShort) && t->note)
fmtprint(fp, " \"%Z\"", t->note);
return 0;
case TFORW:
if(t->sym)
return fmtprint(fp, "undefined %S", t->sym);
return fmtstrcpy(fp, "undefined");
case TUNSAFEPTR:
if(fmtmode == FExp)
return fmtprint(fp, "@\"unsafe\".Pointer");
return fmtprint(fp, "unsafe.Pointer");
}
if(fmtmode == FExp)
fatal("missing %E case during export", t->etype);
// Don't know how to handle - fall back to detailed prints.
return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type);
}
// Statements which may be rendered with a simplestmt as init.
static int
stmtwithinit(int op)
{
switch(op) {
case OIF:
case OFOR:
case OSWITCH:
return 1;
}
return 0;
}
static int
stmtfmt(Fmt *f, Node *n)
{
int complexinit, simpleinit, extrablock;
// some statements allow for an init, but at most one,
// but we may have an arbitrary number added, eg by typecheck
// and inlining. If it doesn't fit the syntax, emit an enclosing
// block starting with the init statements.
// if we can just say "for" n->ninit; ... then do so
simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op);
// otherwise, print the inits as separate statements
complexinit = n->ninit && !simpleinit && (fmtmode != FErr);
// but if it was for if/for/switch, put in an extra surrounding block to limit the scope
extrablock = complexinit && stmtwithinit(n->op);
if(extrablock)
fmtstrcpy(f, "{");
if(complexinit)
fmtprint(f, " %H; ", n->ninit);
switch(n->op){
case ODCL:
fmtprint(f, "var %S %T", n->left->sym, n->left->type);
break;
case ODCLFIELD:
if(n->left)
fmtprint(f, "%N %N", n->left, n->right);
else
fmtprint(f, "%N", n->right);
break;
case OAS:
if(n->colas && !complexinit)
fmtprint(f, "%N := %N", n->left, n->right);
else
fmtprint(f, "%N = %N", n->left, n->right);
break;
case OASOP:
fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right);
break;
case OAS2:
if(n->colas && !complexinit) {
fmtprint(f, "%,H := %,H", n->list, n->rlist);
break;
}
// fallthrough
case OAS2DOTTYPE:
case OAS2FUNC:
case OAS2MAPR:
case OAS2RECV:
fmtprint(f, "%,H = %,H", n->list, n->rlist);
break;
case ORETURN:
fmtprint(f, "return %,H", n->list);
break;
case OPROC:
fmtprint(f, "go %N", n->left);
break;
case ODEFER:
fmtprint(f, "defer %N", n->left);
break;
case OIF:
if(simpleinit)
fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody);
else
fmtprint(f, "if %N { %H }", n->ntest, n->nbody);
if(n->nelse)
fmtprint(f, " else { %H }", n->nelse);
break;
case OFOR:
if(fmtmode == FErr) { // TODO maybe only if FmtShort, same below
fmtstrcpy(f, "for loop");
break;
}
fmtstrcpy(f, "for");
if(simpleinit)
fmtprint(f, " %N;", n->ninit->n);
else if(n->nincr)
fmtstrcpy(f, " ;");
if(n->ntest)
fmtprint(f, " %N", n->ntest);
if(n->nincr)
fmtprint(f, "; %N", n->nincr);
else if(simpleinit)
fmtstrcpy(f, ";");
fmtprint(f, " { %H }", n->nbody);
break;
case ORANGE:
if(fmtmode == FErr) {
fmtstrcpy(f, "for loop");
break;
}
fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody);
break;
case OSELECT:
case OSWITCH:
if(fmtmode == FErr) {
fmtprint(f, "%O statement", n->op);
break;
}
fmtprint(f, "%#O", n->op);
if(simpleinit)
fmtprint(f, " %N;", n->ninit->n);
if(n->ntest)
fmtprint(f, "%N", n->ntest);
fmtprint(f, " { %H }", n->list);
break;
case OCASE:
case OXCASE:
if(n->list)
fmtprint(f, "case %,H: %H", n->list, n->nbody);
else
fmtprint(f, "default: %H", n->nbody);
break;
case OBREAK:
case OCONTINUE:
case OGOTO:
case OFALL:
case OXFALL:
if(n->left)
fmtprint(f, "%#O %N", n->op, n->left);
else
fmtprint(f, "%#O", n->op);
break;
case OEMPTY:
break;
case OLABEL:
fmtprint(f, "%N: ", n->left);
break;
}
if(extrablock)
fmtstrcpy(f, "}");
return 0;
}
static int opprec[] = {
[OAPPEND] = 8,
[OARRAYBYTESTR] = 8,
[OARRAYLIT] = 8,
[OARRAYRUNESTR] = 8,
[OCALLFUNC] = 8,
[OCALLINTER] = 8,
[OCALLMETH] = 8,
[OCALL] = 8,
[OCAP] = 8,
[OCLOSE] = 8,
[OCONVIFACE] = 8,
[OCONVNOP] = 8,
[OCONV] = 8,
[OCOPY] = 8,
[ODELETE] = 8,
[OLEN] = 8,
[OLITERAL] = 8,
[OMAKESLICE] = 8,
[OMAKE] = 8,
[OMAPLIT] = 8,
[ONAME] = 8,
[ONEW] = 8,
[ONONAME] = 8,
[OPACK] = 8,
[OPANIC] = 8,
[OPAREN] = 8,
[OPRINTN] = 8,
[OPRINT] = 8,
[ORECV] = 8,
[ORUNESTR] = 8,
[OSTRARRAYBYTE] = 8,
[OSTRARRAYRUNE] = 8,
[OSTRUCTLIT] = 8,
[OTARRAY] = 8,
[OTCHAN] = 8,
[OTFUNC] = 8,
[OTINTER] = 8,
[OTMAP] = 8,
[OTPAREN] = 8,
[OTSTRUCT] = 8,
[OINDEXMAP] = 8,
[OINDEX] = 8,
[OSLICE] = 8,
[OSLICESTR] = 8,
[OSLICEARR] = 8,
[ODOTINTER] = 8,
[ODOTMETH] = 8,
[ODOTPTR] = 8,
[ODOTTYPE2] = 8,
[ODOTTYPE] = 8,
[ODOT] = 8,
[OXDOT] = 8,
[OPLUS] = 7,
[ONOT] = 7,
[OCOM] = 7,
[OMINUS] = 7,
[OADDR] = 7,
[OIND] = 7,
[OMUL] = 6,
[ODIV] = 6,
[OMOD] = 6,
[OLSH] = 6,
[ORSH] = 6,
[OAND] = 6,
[OANDNOT] = 6,
[OADD] = 5,
[OSUB] = 5,
[OOR] = 5,
[OXOR] = 5,
[OEQ] = 4,
[OLT] = 4,
[OLE] = 4,
[OGE] = 4,
[OGT] = 4,
[ONE] = 4,
[OCMPSTR] = 4,
[OCMPIFACE] = 4,
[OSEND] = 3,
[OANDAND] = 2,
[OOROR] = 1,
// Statements handled by stmtfmt
[OAS] = -1,
[OAS2] = -1,
[OAS2DOTTYPE] = -1,
[OAS2FUNC] = -1,
[OAS2MAPR] = -1,
[OAS2RECV] = -1,
[OASOP] = -1,
[OBREAK] = -1,
[OCASE] = -1,
[OCONTINUE] = -1,
[ODCL] = -1,
[ODCLFIELD] = -1,
[ODEFER] = -1,
[OEMPTY] = -1,
[OFALL] = -1,
[OFOR] = -1,
[OIF] = -1,
[OLABEL] = -1,
[OPROC] = -1,
[ORANGE] = -1,
[ORETURN] = -1,
[OSELECT] = -1,
[OSWITCH] = -1,
[OXCASE] = -1,
[OXFALL] = -1,
[OEND] = 0
};
static int
exprfmt(Fmt *f, Node *n, int prec)
{
int nprec;
NodeList *l;
Type *t;
while(n && n->implicit && (n->op == OIND || n->op == OADDR))
n = n->left;
if(n == N)
return fmtstrcpy(f, "<N>");
nprec = opprec[n->op];
if(n->op == OTYPE && n->sym != S)
nprec = 8;
if(prec > nprec)
return fmtprint(f, "(%N)", n);
switch(n->op) {
case OPAREN:
return fmtprint(f, "(%N)", n->left);
case ODDDARG:
return fmtprint(f, "... argument");
case OREGISTER:
return fmtprint(f, "%R", n->val.u.reg);
case OLITERAL: // this is a bit of a mess
if(fmtmode == FErr && n->sym != S)
return fmtprint(f, "%S", n->sym);
if(n->val.ctype == CTNIL)
n = n->orig; // if this node was a nil decorated with at type, print the original naked nil
if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) {
// Need parens when type begins with what might
// be misinterpreted as a unary operator: * or <-.
if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv))
return fmtprint(f, "(%T)(%V)", n->type, &n->val);
else
return fmtprint(f, "%T(%V)", n->type, &n->val);
}
return fmtprint(f, "%V", &n->val);
case ONAME:
// Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
// but for export, this should be rendered as (*pkg.T).meth.
// These nodes have the special property that they are names with a left OTYPE and a right ONAME.
if(fmtmode == FExp && n->left && n->left->op == OTYPE && n->right && n->right->op == ONAME) {
if(isptr[n->left->type->etype])
return fmtprint(f, "(%T).%hhS", n->left->type, n->right->sym);
else
return fmtprint(f, "%T.%hhS", n->left->type, n->right->sym);
}
//fallthrough
case OPACK:
case ONONAME:
return fmtprint(f, "%S", n->sym);
case OTYPE:
if(n->type == T && n->sym != S)
return fmtprint(f, "%S", n->sym);
return fmtprint(f, "%T", n->type);
case OTARRAY:
if(n->left)
return fmtprint(f, "[]%N", n->left);
return fmtprint(f, "[]%N", n->right); // happens before typecheck
case OTPAREN:
return fmtprint(f, "(%N)", n->left);
case OTMAP:
return fmtprint(f, "map[%N]%N", n->left, n->right);
case OTCHAN:
switch(n->etype) {
case Crecv:
return fmtprint(f, "<-chan %N", n->left);
case Csend:
return fmtprint(f, "chan<- %N", n->left);
default:
if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv)
return fmtprint(f, "chan (%N)", n->left);
else
return fmtprint(f, "chan %N", n->left);
}
case OTSTRUCT:
return fmtprint(f, "<struct>");
case OTINTER:
return fmtprint(f, "<inter>");
case OTFUNC:
return fmtprint(f, "<func>");
case OCLOSURE:
if(fmtmode == FErr)
return fmtstrcpy(f, "func literal");
if(n->nbody)
return fmtprint(f, "%T { %H }", n->type, n->nbody);
return fmtprint(f, "%T { %H }", n->type, n->closure->nbody);
case OCOMPLIT:
if(fmtmode == FErr)
return fmtstrcpy(f, "composite literal");
return fmtprint(f, "%N{ %,H }", n->right, n->list);
case OPTRLIT:
if(fmtmode == FExp && n->left->implicit)
return fmtprint(f, "%N", n->left);
return fmtprint(f, "&%N", n->left);
case OSTRUCTLIT:
if(fmtmode == FExp) { // requires special handling of field names
if(n->implicit)
fmtstrcpy(f, "{");
else
fmtprint(f, "%T{", n->type);
for(l=n->list; l; l=l->next) {
// another special case: if n->left is an embedded field of builtin type,
// it needs to be non-qualified. Can't figure that out in %S, so do it here
if(l->n->left->type->embedded) {
t = l->n->left->type->type;
if(t->sym == S)
t = t->type;
fmtprint(f, " %T:%N", t, l->n->right);
} else
fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right);
if(l->next)
fmtstrcpy(f, ",");
else
fmtstrcpy(f, " ");
}
return fmtstrcpy(f, "}");
}
// fallthrough
case OARRAYLIT:
case OMAPLIT:
if(fmtmode == FErr)
return fmtprint(f, "%T literal", n->type);
if(fmtmode == FExp && n->implicit)
return fmtprint(f, "{ %,H }", n->list);
return fmtprint(f, "%T{ %,H }", n->type, n->list);
case OKEY:
if(n->left && n->right)
return fmtprint(f, "%N:%N", n->left, n->right);
if(!n->left && n->right)
return fmtprint(f, ":%N", n->right);
if(n->left && !n->right)
return fmtprint(f, "%N:", n->left);
return fmtstrcpy(f, ":");
case OXDOT:
case ODOT:
case ODOTPTR:
case ODOTINTER:
case ODOTMETH:
exprfmt(f, n->left, nprec);
if(n->right == N || n->right->sym == S)
fmtstrcpy(f, ".<nil>");
return fmtprint(f, ".%hhS", n->right->sym);
case ODOTTYPE:
case ODOTTYPE2:
exprfmt(f, n->left, nprec);
if(n->right != N)
return fmtprint(f, ".(%N)", n->right);
return fmtprint(f, ".(%T)", n->type);
case OINDEX:
case OINDEXMAP:
case OSLICE:
case OSLICESTR:
case OSLICEARR:
exprfmt(f, n->left, nprec);
return fmtprint(f, "[%N]", n->right);
case OCOPY:
case OCOMPLEX:
return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right);
case OCONV:
case OCONVIFACE:
case OCONVNOP:
case OARRAYBYTESTR:
case OARRAYRUNESTR:
case OSTRARRAYBYTE:
case OSTRARRAYRUNE:
case ORUNESTR:
if(n->type == T || n->type->sym == S)
return fmtprint(f, "(%T)(%N)", n->type, n->left);
if(n->left)
return fmtprint(f, "%T(%N)", n->type, n->left);
return fmtprint(f, "%T(%,H)", n->type, n->list);
case OREAL:
case OIMAG:
case OAPPEND:
case OCAP:
case OCLOSE:
case ODELETE:
case OLEN:
case OMAKE:
case ONEW:
case OPANIC:
case OPRINT:
case OPRINTN:
if(n->left)
return fmtprint(f, "%#O(%N)", n->op, n->left);
if(n->isddd)
return fmtprint(f, "%#O(%,H...)", n->op, n->list);
return fmtprint(f, "%#O(%,H)", n->op, n->list);
case OCALL:
case OCALLFUNC:
case OCALLINTER:
case OCALLMETH:
exprfmt(f, n->left, nprec);
if(n->isddd)
return fmtprint(f, "(%,H...)", n->list);
return fmtprint(f, "(%,H)", n->list);
case OMAKEMAP:
case OMAKECHAN:
case OMAKESLICE:
if(n->list) // pre-typecheck
return fmtprint(f, "make(%T, %,H)", n->type, n->list);
if(n->right)
return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right);
if(n->left)
return fmtprint(f, "make(%T, %N)", n->type, n->left);
return fmtprint(f, "make(%T)", n->type);
// Unary
case OPLUS:
case OMINUS:
case OADDR:
case OCOM:
case OIND:
case ONOT:
case ORECV:
if(n->left->op == n->op)
fmtprint(f, "%#O ", n->op);
else
fmtprint(f, "%#O", n->op);
return exprfmt(f, n->left, nprec+1);
// Binary
case OADD:
case OADDSTR:
case OAND:
case OANDAND:
case OANDNOT:
case ODIV:
case OEQ:
case OGE:
case OGT:
case OLE:
case OLT:
case OLSH:
case OMOD:
case OMUL:
case ONE:
case OOR:
case OOROR:
case ORSH:
case OSEND:
case OSUB:
case OXOR:
exprfmt(f, n->left, nprec);
fmtprint(f, " %#O ", n->op);
exprfmt(f, n->right, nprec+1);
return 0;
case OCMPSTR:
case OCMPIFACE:
exprfmt(f, n->left, nprec);
fmtprint(f, " %#O ", n->etype);
exprfmt(f, n->right, nprec+1);
return 0;
}
return fmtprint(f, "<node %O>", n->op);
}
static int
nodefmt(Fmt *f, Node *n)
{
Type *t;
t = n->type;
// we almost always want the original, except in export mode for literals
// this saves the importer some work, and avoids us having to redo some
// special casing for package unsafe
if((fmtmode != FExp || n->op != OLITERAL) && n->orig != N)
n = n->orig;
if(f->flags&FmtLong && t != T) {
if(t->etype == TNIL)
return fmtprint(f, "nil");
else
return fmtprint(f, "%N (type %T)", n, t);
}
// TODO inlining produces expressions with ninits. we can't print these yet.
if(opprec[n->op] < 0)
return stmtfmt(f, n);
return exprfmt(f, n, 0);
}
static int dumpdepth;
static void
indent(Fmt *fp)
{
int i;
fmtstrcpy(fp, "\n");
for(i = 0; i < dumpdepth; ++i)
fmtstrcpy(fp, ". ");
}
static int
nodedump(Fmt *fp, Node *n)
{
int recur;
if(n == N)
return 0;
recur = !(fp->flags&FmtShort);
if(recur) {
indent(fp);
if(dumpdepth > 10)
return fmtstrcpy(fp, "...");
if(n->ninit != nil) {
fmtprint(fp, "%O-init%H", n->op, n->ninit);
indent(fp);
}
}
// fmtprint(fp, "[%p]", n);
switch(n->op) {
default:
fmtprint(fp, "%O%J", n->op, n);
break;
case OREGISTER:
fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
break;
case OLITERAL:
fmtprint(fp, "%O-%V%J", n->op, &n->val, n);
break;
case ONAME:
case ONONAME:
if(n->sym != S)
fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
else
fmtprint(fp, "%O%J", n->op, n);
break;
case OASOP:
fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
break;
case OTYPE:
fmtprint(fp, "%O %S%J type=%T", n->op, n->sym, n, n->type);
if(recur && n->type == T && n->ntype) {
indent(fp);
fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
}
break;
}
if(n->sym != S && n->op != ONAME)
fmtprint(fp, " %S G%d", n->sym, n->vargen);
if(n->type != T)
fmtprint(fp, " %T", n->type);
if(recur) {
if(n->left)
fmtprint(fp, "%N", n->left);
if(n->right)
fmtprint(fp, "%N", n->right);
if(n->list) {
indent(fp);
fmtprint(fp, "%O-list%H", n->op, n->list);
}
if(n->rlist) {
indent(fp);
fmtprint(fp, "%O-rlist%H", n->op, n->rlist);
}
if(n->ntest) {
indent(fp);
fmtprint(fp, "%O-test%N", n->op, n->ntest);
}
if(n->nbody) {
indent(fp);
fmtprint(fp, "%O-body%H", n->op, n->nbody);
}
if(n->nelse) {
indent(fp);
fmtprint(fp, "%O-else%H", n->op, n->nelse);
}
if(n->nincr) {
indent(fp);
fmtprint(fp, "%O-incr%N", n->op, n->nincr);
}
}
return 0;
}
// Fmt "%S": syms
// Flags: "%hS" suppresses qualifying with package
static int
Sconv(Fmt *fp)
{
Sym *s;
int r, sm;
unsigned long sf;
s = va_arg(fp->args, Sym*);
if(s == S)
return fmtstrcpy(fp, "<S>");
if(s->name && s->name[0] == '_' && s->name[1] == '\0')
return fmtstrcpy(fp, "_");
sf = fp->flags;
sm = setfmode(&fp->flags);
r = symfmt(fp, s);
fp->flags = sf;
fmtmode = sm;
return r;
}
// Fmt "%T": types.
// Flags: 'l' print definition, not name
// 'h' omit 'func' and receiver from function types, short type names
// 'u' package name, not prefix (FTypeId mode, sticky)
static int
Tconv(Fmt *fp)
{
Type *t;
int r, sm;
unsigned long sf;
t = va_arg(fp->args, Type*);
if(t == T)
return fmtstrcpy(fp, "<T>");
if(t->trecur > 4)
return fmtstrcpy(fp, "<...>");
t->trecur++;
sf = fp->flags;
sm = setfmode(&fp->flags);
if(fmtmode == FTypeId && (sf&FmtUnsigned))
fmtpkgpfx++;
if(fmtpkgpfx)
fp->flags |= FmtUnsigned;
r = typefmt(fp, t);
if(fmtmode == FTypeId && (sf&FmtUnsigned))
fmtpkgpfx--;
fp->flags = sf;
fmtmode = sm;
t->trecur--;
return r;
}
// Fmt '%N': Nodes.
// Flags: 'l' suffix with "(type %T)" where possible
// '+h' in debug mode, don't recurse, no multiline output
static int
Nconv(Fmt *fp)
{
Node *n;
int r, sm;
unsigned long sf;
n = va_arg(fp->args, Node*);
if(n == N)
return fmtstrcpy(fp, "<N>");
sf = fp->flags;
sm = setfmode(&fp->flags);
r = -1;
switch(fmtmode) {
case FErr:
case FExp:
r = nodefmt(fp, n);
break;
case FDbg:
dumpdepth++;
r = nodedump(fp, n);
dumpdepth--;
break;
default:
fatal("unhandled %%N mode");
}
fp->flags = sf;
fmtmode = sm;
return r;
}
// Fmt '%H': NodeList.
// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
static int
Hconv(Fmt *fp)
{
NodeList *l;
int r, sm;
unsigned long sf;
char *sep;
l = va_arg(fp->args, NodeList*);
if(l == nil && fmtmode == FDbg)
return fmtstrcpy(fp, "<nil>");
sf = fp->flags;
sm = setfmode(&fp->flags);
r = 0;
sep = "; ";
if(fmtmode == FDbg)
sep = "\n";
else if(fp->flags & FmtComma)
sep = ", ";
for(;l; l=l->next) {
r += fmtprint(fp, "%N", l->n);
if(l->next)
r += fmtstrcpy(fp, sep);
}
fp->flags = sf;
fmtmode = sm;
return r;
}
void
fmtinstallgo(void)
{
fmtmode = FErr;
fmtinstall('E', Econv); // etype opcodes
fmtinstall('J', Jconv); // all the node flags
fmtinstall('H', Hconv); // node lists
fmtinstall('L', Lconv); // line number
fmtinstall('N', Nconv); // node pointer
fmtinstall('O', Oconv); // node opcodes
fmtinstall('S', Sconv); // sym pointer
fmtinstall('T', Tconv); // type pointer
fmtinstall('V', Vconv); // val pointer
fmtinstall('Z', Zconv); // escaped string
// These are in mparith1.c
fmtinstall('B', Bconv); // big numbers
fmtinstall('F', Fconv); // big float numbers
}
void
dumplist(char *s, NodeList *l)
{
print("%s\n%+H\n", s, l);
}
void
dump(char *s, Node *n)
{
print("%s [%p]\n%+N\n", s, n, n);
}