Skip to content

Commit 9523b4d

Browse files
author
Luuk van Dijk
committed
gc: fix infinite recursion for embedded interfaces
Fixes #1909 R=rsc, gri CC=golang-dev https://golang.org/cl/5523047
1 parent 8e99016 commit 9523b4d

File tree

6 files changed

+73
-21
lines changed

6 files changed

+73
-21
lines changed

src/cmd/gc/dcl.c

+11-2
Original file line numberDiff line numberDiff line change
@@ -940,19 +940,28 @@ interfacefield(Node *n)
940940
Type*
941941
tointerface(NodeList *l)
942942
{
943-
Type *t, *f, **tp, *t1;
943+
Type *t, *f, **tp, **otp, *t1;
944944

945945
t = typ(TINTER);
946+
t->orig = typ(TINTER);
946947

947-
for(tp = &t->type; l; l=l->next) {
948+
tp = &t->type;
949+
otp = &t->orig->type;
950+
951+
for(; l; l=l->next) {
948952
f = interfacefield(l->n);
953+
*otp = typ(TFIELD);
954+
**otp = *f;
955+
otp = &(*otp)->down;
956+
949957
if (l->n->left == N && f->type->etype == TINTER) {
950958
// embedded interface, inline methods
951959
for(t1=f->type->type; t1; t1=t1->down) {
952960
f = typ(TFIELD);
953961
f->type = t1->type;
954962
f->broke = t1->broke;
955963
f->sym = t1->sym;
964+
f->embedded = 1;
956965
if(f->sym)
957966
f->nname = newname(f->sym);
958967
*tp = f;

src/cmd/gc/export.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,13 @@ dumpexporttype(Type *t)
241241
if(t->sym != S && t->etype != TFIELD)
242242
dumppkg(t->sym->pkg);
243243

244+
// fmt will print the ->orig of an interface, which has the original embedded interfaces.
245+
// be sure to dump them here
246+
if(t->etype == TINTER)
247+
for(f=t->orig->type; f; f=f->down)
248+
if(f->sym == S)
249+
dumpexporttype(f->type);
250+
244251
dumpexporttype(t->type);
245252
dumpexporttype(t->down);
246253

@@ -470,8 +477,8 @@ importtype(Type *pt, Type *t)
470477
pt->sym->lastlineno = parserline();
471478
declare(n, PEXTERN);
472479
checkwidth(pt);
473-
} else if(!eqtype(pt->orig, t))
474-
yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt->orig, t);
480+
} else if(!eqtype(pt->orig, t->orig))
481+
yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt, t);
475482

476483
if(debug['E'])
477484
print("import type %T %lT\n", pt, t);

src/cmd/gc/fmt.c

+16-2
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ goopnames[] =
195195
[OCONTINUE] = "continue",
196196
[OCOPY] = "copy",
197197
[ODEC] = "--",
198+
[ODELETE] = "delete",
198199
[ODEFER] = "defer",
199200
[ODIV] = "/",
200201
[OEQ] = "==",
@@ -639,9 +640,15 @@ typefmt(Fmt *fp, Type *t)
639640
return fmtprint(fp, "map[%T]%T", t->down, t->type);
640641

641642
case TINTER:
643+
t = t->orig;
642644
fmtstrcpy(fp, "interface {");
643645
for(t1=t->type; t1!=T; t1=t1->down)
644-
if(exportname(t1->sym->name)) {
646+
if(!t1->sym) {
647+
if(t1->down)
648+
fmtprint(fp, " %T;", t1->type);
649+
else
650+
fmtprint(fp, " %T ", t1->type);
651+
} else if(exportname(t1->sym->name)) {
645652
if(t1->down)
646653
fmtprint(fp, " %hS%hT;", t1->sym, t1->type);
647654
else
@@ -946,6 +953,7 @@ static int opprec[] = {
946953
[OCONVNOP] = 8,
947954
[OCONV] = 8,
948955
[OCOPY] = 8,
956+
[ODELETE] = 8,
949957
[OLEN] = 8,
950958
[OLITERAL] = 8,
951959
[OMAKESLICE] = 8,
@@ -1010,6 +1018,7 @@ static int opprec[] = {
10101018
[OGT] = 4,
10111019
[ONE] = 4,
10121020
[OCMPSTR] = 4,
1021+
[OCMPIFACE] = 4,
10131022

10141023
[OSEND] = 3,
10151024
[OANDAND] = 2,
@@ -1218,6 +1227,7 @@ exprfmt(Fmt *f, Node *n, int prec)
12181227
case OAPPEND:
12191228
case OCAP:
12201229
case OCLOSE:
1230+
case ODELETE:
12211231
case OLEN:
12221232
case OMAKE:
12231233
case ONEW:
@@ -1288,6 +1298,7 @@ exprfmt(Fmt *f, Node *n, int prec)
12881298
return 0;
12891299

12901300
case OCMPSTR:
1301+
case OCMPIFACE:
12911302
exprfmt(f, n->left, nprec);
12921303
fmtprint(f, " %#O ", n->etype);
12931304
exprfmt(f, n->right, nprec+1);
@@ -1303,8 +1314,10 @@ nodefmt(Fmt *f, Node *n)
13031314
Type *t;
13041315

13051316
t = n->type;
1306-
if(n->orig == N)
1317+
if(n->orig == N) {
1318+
n->orig = n;
13071319
fatal("node with no orig %N", n);
1320+
}
13081321

13091322
// we almost always want the original, except in export mode for literals
13101323
// this saves the importer some work, and avoids us having to redo some
@@ -1359,6 +1372,7 @@ nodedump(Fmt *fp, Node *n)
13591372
indent(fp);
13601373
}
13611374
}
1375+
fmtprint(fp, "[%p]", n);
13621376

13631377
switch(n->op) {
13641378
default:

src/cmd/gc/go.y

+5-1
Original file line numberDiff line numberDiff line change
@@ -1620,7 +1620,7 @@ non_dcl_stmt:
16201620
$$->list = $2;
16211621
if($$->list == nil && curfn != N) {
16221622
NodeList *l;
1623-
1623+
16241624
for(l=curfn->dcl; l; l=l->next) {
16251625
if(l->n->class == PPARAM)
16261626
continue;
@@ -1953,6 +1953,10 @@ hidden_interfacedcl:
19531953
{
19541954
$$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
19551955
}
1956+
| hidden_type
1957+
{
1958+
$$ = nod(ODCLFIELD, N, typenod($1));
1959+
}
19561960

19571961
ohidden_funres:
19581962
{

src/pkg/exp/types/gcimporter.go

+17-14
Original file line numberDiff line numberDiff line change
@@ -460,29 +460,32 @@ func (p *gcParser) parseSignature() *Func {
460460
return &Func{Params: params, Results: results, IsVariadic: isVariadic}
461461
}
462462

463-
// MethodSpec = ( identifier | ExportedName ) Signature .
463+
// MethodOrEmbedSpec = Name [ Signature ] .
464464
//
465-
func (p *gcParser) parseMethodSpec() *ast.Object {
466-
if p.tok == scanner.Ident {
467-
p.expect(scanner.Ident)
468-
} else {
469-
p.parseExportedName()
465+
func (p *gcParser) parseMethodOrEmbedSpec() *ast.Object {
466+
p.parseName()
467+
if p.tok == '(' {
468+
p.parseSignature()
469+
// TODO(gri) compute method object
470+
return ast.NewObj(ast.Fun, "_")
470471
}
471-
p.parseSignature()
472-
473-
// TODO(gri) compute method object
474-
return ast.NewObj(ast.Fun, "_")
472+
// TODO lookup name and return that type
473+
return ast.NewObj(ast.Typ, "_")
475474
}
476475

477-
// InterfaceType = "interface" "{" [ MethodList ] "}" .
478-
// MethodList = MethodSpec { ";" MethodSpec } .
476+
// InterfaceType = "interface" "{" [ MethodOrEmbedList ] "}" .
477+
// MethodOrEmbedList = MethodOrEmbedSpec { ";" MethodOrEmbedSpec } .
479478
//
480479
func (p *gcParser) parseInterfaceType() Type {
481480
var methods ObjList
482481

483482
parseMethod := func() {
484-
meth := p.parseMethodSpec()
485-
methods = append(methods, meth)
483+
switch m := p.parseMethodOrEmbedSpec(); m.Kind {
484+
case ast.Typ:
485+
// TODO expand embedded methods
486+
case ast.Fun:
487+
methods = append(methods, m)
488+
}
486489
}
487490

488491
p.expectKeyword("interface")

test/fixedbugs/bug395.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// $G $D/$F.go || echo "Bug395"
2+
3+
// Copyright 2011 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Issue 1909
8+
// Would OOM due to exponential recursion on Foo's expanded methodset in nodefmt
9+
package test
10+
11+
type Foo interface {
12+
Bar() interface{Foo}
13+
Baz() interface{Foo}
14+
Bug() interface{Foo}
15+
}

0 commit comments

Comments
 (0)