mirror of
https://github.com/golang/go
synced 2024-10-14 11:53:56 +00:00
cmd/gc: annotate local variables with unique ids for inlining
Avoids problems with local declarations shadowing other names. We write a more explicit form than the incoming program, so there may be additional type annotations. For example: int := "hello" j := 2 would normally turn into var int string = "hello" var j int = 2 but the int variable shadows the int type in the second line. This CL marks all local variables with a per-function sequence number, so that this would instead be: var int·1 string = "hello" var j·2 int = 2 Fixes #4326. R=ken2 CC=golang-dev https://golang.org/cl/6816100
This commit is contained in:
parent
c6f363b22a
commit
cb856adea9
|
@ -163,6 +163,8 @@ redeclare(Sym *s, char *where)
|
|||
s, where, s->lastlineno);
|
||||
}
|
||||
|
||||
static int vargen;
|
||||
|
||||
/*
|
||||
* declare individual names - var, typ, const
|
||||
*/
|
||||
|
@ -171,7 +173,7 @@ declare(Node *n, int ctxt)
|
|||
{
|
||||
Sym *s;
|
||||
int gen;
|
||||
static int typegen, vargen;
|
||||
static int typegen;
|
||||
|
||||
if(ctxt == PDISCARD)
|
||||
return;
|
||||
|
@ -198,7 +200,7 @@ declare(Node *n, int ctxt)
|
|||
curfn->dcl = list(curfn->dcl, n);
|
||||
if(n->op == OTYPE)
|
||||
gen = ++typegen;
|
||||
else if(n->op == ONAME)
|
||||
else if(n->op == ONAME && ctxt == PAUTO && strstr(s->name, "·") == nil)
|
||||
gen = ++vargen;
|
||||
pushdcl(s);
|
||||
n->curfn = curfn;
|
||||
|
@ -522,7 +524,7 @@ ifacedcl(Node *n)
|
|||
if(n->op != ODCLFIELD || n->right == N)
|
||||
fatal("ifacedcl");
|
||||
|
||||
dclcontext = PAUTO;
|
||||
dclcontext = PPARAM;
|
||||
markdcl();
|
||||
funcdepth++;
|
||||
n->outer = curfn;
|
||||
|
@ -533,6 +535,7 @@ ifacedcl(Node *n)
|
|||
// seen the body of a function but since an interface
|
||||
// field declaration does not have a body, we must
|
||||
// call it now to pop the current declaration context.
|
||||
dclcontext = PAUTO;
|
||||
funcbody(n);
|
||||
}
|
||||
|
||||
|
@ -574,6 +577,11 @@ funcargs(Node *nt)
|
|||
if(nt->op != OTFUNC)
|
||||
fatal("funcargs %O", nt->op);
|
||||
|
||||
// re-start the variable generation number
|
||||
// we want to use small numbers for the return variables,
|
||||
// so let them have the chunk starting at 1.
|
||||
vargen = count(nt->rlist);
|
||||
|
||||
// declare the receiver and in arguments.
|
||||
// no n->defn because type checking of func header
|
||||
// will not fill in the types until later
|
||||
|
@ -585,6 +593,8 @@ funcargs(Node *nt)
|
|||
n->left->op = ONAME;
|
||||
n->left->ntype = n->right;
|
||||
declare(n->left, PPARAM);
|
||||
if(dclcontext == PAUTO)
|
||||
n->left->vargen = ++vargen;
|
||||
}
|
||||
}
|
||||
for(l=nt->list; l; l=l->next) {
|
||||
|
@ -595,6 +605,8 @@ funcargs(Node *nt)
|
|||
n->left->op = ONAME;
|
||||
n->left->ntype = n->right;
|
||||
declare(n->left, PPARAM);
|
||||
if(dclcontext == PAUTO)
|
||||
n->left->vargen = ++vargen;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -630,7 +642,8 @@ funcargs(Node *nt)
|
|||
|
||||
n->left->ntype = n->right;
|
||||
declare(n->left, PPARAMOUT);
|
||||
n->left->vargen = i++;
|
||||
if(dclcontext == PAUTO)
|
||||
n->left->vargen = ++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -997,14 +997,14 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
|
|||
e->pdepth++;
|
||||
|
||||
// Input parameter flowing to output parameter?
|
||||
if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen < 20) {
|
||||
if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) {
|
||||
if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) {
|
||||
if(src->esc != EscScope && src->esc != EscHeap) {
|
||||
if(debug['m'])
|
||||
warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym);
|
||||
if((src->esc&EscMask) != EscReturn)
|
||||
src->esc = EscReturn;
|
||||
src->esc |= 1<<(dst->vargen + EscBits);
|
||||
src->esc |= 1<<((dst->vargen-1) + EscBits);
|
||||
}
|
||||
goto recurse;
|
||||
}
|
||||
|
|
|
@ -724,7 +724,9 @@ typefmt(Fmt *fp, Type *t)
|
|||
s = S;
|
||||
|
||||
if(s != S && !t->embedded) {
|
||||
if(fp->flags&FmtLong)
|
||||
if(t->funarg)
|
||||
fmtprint(fp, "%N ", t->nname);
|
||||
else if(fp->flags&FmtLong)
|
||||
fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg)
|
||||
else
|
||||
fmtprint(fp, "%S ", s);
|
||||
|
@ -802,6 +804,15 @@ stmtfmt(Fmt *f, Node *n)
|
|||
|
||||
switch(n->op){
|
||||
case ODCL:
|
||||
if(fmtmode == FExp) {
|
||||
switch(n->left->class&~PHEAP) {
|
||||
case PPARAM:
|
||||
case PPARAMOUT:
|
||||
case PAUTO:
|
||||
fmtprint(f, "var %N %T", n->left, n->left->type);
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
fmtprint(f, "var %S %T", n->left->sym, n->left->type);
|
||||
break;
|
||||
|
||||
|
@ -939,6 +950,7 @@ stmtfmt(Fmt *f, Node *n)
|
|||
break;
|
||||
|
||||
}
|
||||
ret:
|
||||
|
||||
if(extrablock)
|
||||
fmtstrcpy(f, "}");
|
||||
|
@ -1111,6 +1123,15 @@ exprfmt(Fmt *f, Node *n, int prec)
|
|||
return fmtprint(f, "%V", &n->val);
|
||||
|
||||
case ONAME:
|
||||
// Special case: name used as local variable in export.
|
||||
switch(n->class&~PHEAP){
|
||||
case PAUTO:
|
||||
case PPARAM:
|
||||
case PPARAMOUT:
|
||||
if(fmtmode == FExp && n->sym && !isblanksym(n->sym) && n->vargen > 0)
|
||||
return fmtprint(f, "%S·%d", n->sym, n->vargen);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -122,7 +122,7 @@ var importedObjectTests = []struct {
|
|||
{"math.Pi", ast.Con, "untyped float"},
|
||||
{"io.Reader", ast.Typ, "interface{Read(p []byte) (n int, err error)}"},
|
||||
{"io.ReadWriter", ast.Typ, "interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
|
||||
{"math.Sin", ast.Fun, "func(x float64) (_ float64)"},
|
||||
{"math.Sin", ast.Fun, "func(x·2 float64) (_ float64)"},
|
||||
// TODO(gri) add more tests
|
||||
}
|
||||
|
||||
|
|
12
test/fixedbugs/issue4326.dir/p1.go
Normal file
12
test/fixedbugs/issue4326.dir/p1.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package p1
|
||||
|
||||
type O map[string]map[string]string
|
||||
|
||||
func (opts O) RemoveOption(sect, opt string) bool {
|
||||
if _, ok := opts[sect]; !ok {
|
||||
return false
|
||||
}
|
||||
_, ok := opts[sect][opt]
|
||||
delete(opts[sect], opt)
|
||||
return ok
|
||||
}
|
5
test/fixedbugs/issue4326.dir/p2.go
Normal file
5
test/fixedbugs/issue4326.dir/p2.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package p2
|
||||
|
||||
import "./p1"
|
||||
|
||||
func NewO() p1.O { return nil }
|
8
test/fixedbugs/issue4326.dir/q1.go
Normal file
8
test/fixedbugs/issue4326.dir/q1.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package q1
|
||||
|
||||
func Deref(typ interface{}) interface{} {
|
||||
if typ, ok := typ.(*int); ok {
|
||||
return *typ
|
||||
}
|
||||
return typ
|
||||
}
|
11
test/fixedbugs/issue4326.dir/q2.go
Normal file
11
test/fixedbugs/issue4326.dir/q2.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package main
|
||||
|
||||
import "./q1"
|
||||
|
||||
func main() {
|
||||
x := 1
|
||||
y := q1.Deref(&x)
|
||||
if y != 1 {
|
||||
panic("y != 1")
|
||||
}
|
||||
}
|
7
test/fixedbugs/issue4326.dir/z.go
Normal file
7
test/fixedbugs/issue4326.dir/z.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package main
|
||||
|
||||
import "./p2"
|
||||
|
||||
func main() {
|
||||
p2.NewO().RemoveOption("hello", "world")
|
||||
}
|
9
test/fixedbugs/issue4326.go
Normal file
9
test/fixedbugs/issue4326.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// compiledir
|
||||
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Printing local variables in inliner shadows global names.
|
||||
|
||||
package ignored
|
Loading…
Reference in a new issue