mirror of
https://github.com/golang/go
synced 2024-11-24 15:00:14 -07:00
cmd/gc: fix type checking loop
CL 4313064 fixed its test case but did not address a general enough problem: type T1 struct { F *T2 } type T2 T1 type T3 T2 could still end up copying the definition of T1 for T2 before T1 was done being evaluated, or T3 before T2 was done. In order to propagate the updates correctly, record a copy of an incomplete type for re-execution once the type is completed. Roll back CL 4313064. Fixes #3709. R=ken2 CC=golang-dev, lstoakes https://golang.org/cl/6301059
This commit is contained in:
parent
f18ced3fc9
commit
6363fc5aa6
@ -182,6 +182,9 @@ struct Type
|
|||||||
|
|
||||||
int32 maplineno; // first use of TFORW as map key
|
int32 maplineno; // first use of TFORW as map key
|
||||||
int32 embedlineno; // first use of TFORW as embedded type
|
int32 embedlineno; // first use of TFORW as embedded type
|
||||||
|
|
||||||
|
// for TFORW, where to copy the eventual value to
|
||||||
|
NodeList *copyto;
|
||||||
};
|
};
|
||||||
#define T ((Type*)0)
|
#define T ((Type*)0)
|
||||||
|
|
||||||
@ -1250,9 +1253,7 @@ int islvalue(Node *n);
|
|||||||
Node* typecheck(Node **np, int top);
|
Node* typecheck(Node **np, int top);
|
||||||
void typechecklist(NodeList *l, int top);
|
void typechecklist(NodeList *l, int top);
|
||||||
Node* typecheckdef(Node *n);
|
Node* typecheckdef(Node *n);
|
||||||
void resumetypecopy(void);
|
|
||||||
void copytype(Node *n, Type *t);
|
void copytype(Node *n, Type *t);
|
||||||
void defertypecopy(Node *n, Type *t);
|
|
||||||
void queuemethod(Node *n);
|
void queuemethod(Node *n);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -349,7 +349,6 @@ main(int argc, char *argv[])
|
|||||||
for(l=xtop; l; l=l->next)
|
for(l=xtop; l; l=l->next)
|
||||||
if(l->n->op == ODCL || l->n->op == OAS)
|
if(l->n->op == ODCL || l->n->op == OAS)
|
||||||
typecheck(&l->n, Etop);
|
typecheck(&l->n, Etop);
|
||||||
resumetypecopy();
|
|
||||||
resumecheckwidth();
|
resumecheckwidth();
|
||||||
|
|
||||||
// Phase 3: Type check function bodies.
|
// Phase 3: Type check function bodies.
|
||||||
|
@ -31,7 +31,6 @@ static void checkassign(Node*);
|
|||||||
static void checkassignlist(NodeList*);
|
static void checkassignlist(NodeList*);
|
||||||
static void stringtoarraylit(Node**);
|
static void stringtoarraylit(Node**);
|
||||||
static Node* resolve(Node*);
|
static Node* resolve(Node*);
|
||||||
static Type* getforwtype(Node*);
|
|
||||||
|
|
||||||
static NodeList* typecheckdefstack;
|
static NodeList* typecheckdefstack;
|
||||||
|
|
||||||
@ -237,7 +236,7 @@ typecheck1(Node **np, int top)
|
|||||||
Node *n, *l, *r;
|
Node *n, *l, *r;
|
||||||
NodeList *args;
|
NodeList *args;
|
||||||
int ok, ntop;
|
int ok, ntop;
|
||||||
Type *t, *tp, *ft, *missing, *have, *badtype;
|
Type *t, *tp, *missing, *have, *badtype;
|
||||||
Val v;
|
Val v;
|
||||||
char *why;
|
char *why;
|
||||||
|
|
||||||
@ -249,10 +248,6 @@ typecheck1(Node **np, int top)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// a dance to handle forward-declared recursive pointer types.
|
|
||||||
if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T)
|
|
||||||
defertypecopy(n, ft);
|
|
||||||
|
|
||||||
typecheckdef(n);
|
typecheckdef(n);
|
||||||
n->realtype = n->type;
|
n->realtype = n->type;
|
||||||
if(n->op == ONONAME)
|
if(n->op == ONONAME)
|
||||||
@ -2616,26 +2611,6 @@ stringtoarraylit(Node **np)
|
|||||||
*np = nn;
|
*np = nn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Type*
|
|
||||||
getforwtype(Node *n)
|
|
||||||
{
|
|
||||||
Node *f1, *f2;
|
|
||||||
|
|
||||||
for(f2=n; ; n=n->ntype) {
|
|
||||||
if((n = resolve(n)) == N || n->op != OTYPE)
|
|
||||||
return T;
|
|
||||||
|
|
||||||
if(n->type != T && n->type->etype == TFORW)
|
|
||||||
return n->type;
|
|
||||||
|
|
||||||
// Check for ntype cycle.
|
|
||||||
if((f2 = resolve(f2)) != N && (f1 = resolve(f2->ntype)) != N) {
|
|
||||||
f2 = resolve(f1->ntype);
|
|
||||||
if(f1 == n || f2 == n)
|
|
||||||
return T;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntypecheckdeftype;
|
static int ntypecheckdeftype;
|
||||||
static NodeList *methodqueue;
|
static NodeList *methodqueue;
|
||||||
@ -2673,49 +2648,24 @@ domethod(Node *n)
|
|||||||
checkwidth(n->type);
|
checkwidth(n->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct NodeTypeList NodeTypeList;
|
static NodeList *mapqueue;
|
||||||
struct NodeTypeList {
|
|
||||||
Node *n;
|
|
||||||
Type *t;
|
|
||||||
NodeTypeList *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
static NodeTypeList *dntq;
|
|
||||||
static NodeTypeList *dntend;
|
|
||||||
|
|
||||||
void
|
|
||||||
defertypecopy(Node *n, Type *t)
|
|
||||||
{
|
|
||||||
NodeTypeList *ntl;
|
|
||||||
|
|
||||||
if(n == N || t == T)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ntl = mal(sizeof *ntl);
|
|
||||||
ntl->n = n;
|
|
||||||
ntl->t = t;
|
|
||||||
ntl->next = nil;
|
|
||||||
|
|
||||||
if(dntq == nil)
|
|
||||||
dntq = ntl;
|
|
||||||
else
|
|
||||||
dntend->next = ntl;
|
|
||||||
|
|
||||||
dntend = ntl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
resumetypecopy(void)
|
|
||||||
{
|
|
||||||
NodeTypeList *l;
|
|
||||||
|
|
||||||
for(l=dntq; l; l=l->next)
|
|
||||||
copytype(l->n, l->t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
copytype(Node *n, Type *t)
|
copytype(Node *n, Type *t)
|
||||||
{
|
{
|
||||||
|
int maplineno, embedlineno, lno;
|
||||||
|
NodeList *l;
|
||||||
|
|
||||||
|
if(t->etype == TFORW) {
|
||||||
|
// This type isn't computed yet; when it is, update n.
|
||||||
|
t->copyto = list(t->copyto, n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
maplineno = n->type->maplineno;
|
||||||
|
embedlineno = n->type->embedlineno;
|
||||||
|
|
||||||
|
l = n->type->copyto;
|
||||||
*n->type = *t;
|
*n->type = *t;
|
||||||
|
|
||||||
t = n->type;
|
t = n->type;
|
||||||
@ -2728,12 +2678,32 @@ copytype(Node *n, Type *t)
|
|||||||
t->nod = N;
|
t->nod = N;
|
||||||
t->printed = 0;
|
t->printed = 0;
|
||||||
t->deferwidth = 0;
|
t->deferwidth = 0;
|
||||||
|
t->copyto = nil;
|
||||||
|
|
||||||
|
// Update nodes waiting on this type.
|
||||||
|
for(; l; l=l->next)
|
||||||
|
copytype(l->n, t);
|
||||||
|
|
||||||
|
// Double-check use of type as embedded type.
|
||||||
|
lno = lineno;
|
||||||
|
if(embedlineno) {
|
||||||
|
lineno = embedlineno;
|
||||||
|
if(isptr[t->etype])
|
||||||
|
yyerror("embedded type cannot be a pointer");
|
||||||
|
}
|
||||||
|
lineno = lno;
|
||||||
|
|
||||||
|
// Queue check for map until all the types are done settling.
|
||||||
|
if(maplineno) {
|
||||||
|
t->maplineno = maplineno;
|
||||||
|
mapqueue = list(mapqueue, n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
typecheckdeftype(Node *n)
|
typecheckdeftype(Node *n)
|
||||||
{
|
{
|
||||||
int maplineno, embedlineno, lno;
|
int lno;
|
||||||
Type *t;
|
Type *t;
|
||||||
NodeList *l;
|
NodeList *l;
|
||||||
|
|
||||||
@ -2752,26 +2722,12 @@ typecheckdeftype(Node *n)
|
|||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
maplineno = n->type->maplineno;
|
|
||||||
embedlineno = n->type->embedlineno;
|
|
||||||
|
|
||||||
// copy new type and clear fields
|
// copy new type and clear fields
|
||||||
// that don't come along.
|
// that don't come along.
|
||||||
// anything zeroed here must be zeroed in
|
// anything zeroed here must be zeroed in
|
||||||
// typedcl2 too.
|
// typedcl2 too.
|
||||||
copytype(n, t);
|
copytype(n, t);
|
||||||
|
|
||||||
// double-check use of type as map key.
|
|
||||||
if(maplineno) {
|
|
||||||
lineno = maplineno;
|
|
||||||
maptype(n->type, types[TBOOL]);
|
|
||||||
}
|
|
||||||
if(embedlineno) {
|
|
||||||
lineno = embedlineno;
|
|
||||||
if(isptr[t->etype])
|
|
||||||
yyerror("embedded type cannot be a pointer");
|
|
||||||
}
|
|
||||||
|
|
||||||
ret:
|
ret:
|
||||||
lineno = lno;
|
lineno = lno;
|
||||||
|
|
||||||
@ -2784,6 +2740,11 @@ ret:
|
|||||||
for(; l; l=l->next)
|
for(; l; l=l->next)
|
||||||
domethod(l->n);
|
domethod(l->n);
|
||||||
}
|
}
|
||||||
|
for(l=mapqueue; l; l=l->next) {
|
||||||
|
lineno = l->n->type->maplineno;
|
||||||
|
maptype(l->n->type, types[TBOOL]);
|
||||||
|
}
|
||||||
|
lineno = lno;
|
||||||
}
|
}
|
||||||
ntypecheckdeftype--;
|
ntypecheckdeftype--;
|
||||||
}
|
}
|
||||||
|
17
test/fixedbugs/bug443.go
Normal file
17
test/fixedbugs/bug443.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// compile
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Was failing to compile with 'invalid receiver' due to
|
||||||
|
// incomplete type definition evaluation. Issue 3709.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type T1 struct { F *T2 }
|
||||||
|
type T2 T1
|
||||||
|
|
||||||
|
type T3 T2
|
||||||
|
func (*T3) M() // was invalid receiver
|
||||||
|
|
18
test/map1.go
18
test/map1.go
@ -41,4 +41,22 @@ var (
|
|||||||
_ map[[]int]v // ERROR "invalid map key"
|
_ map[[]int]v // ERROR "invalid map key"
|
||||||
_ map[func()]v // ERROR "invalid map key"
|
_ map[func()]v // ERROR "invalid map key"
|
||||||
_ map[map[int]int]v // ERROR "invalid map key"
|
_ map[map[int]int]v // ERROR "invalid map key"
|
||||||
|
_ map[T1]v // ERROR "invalid map key"
|
||||||
|
_ map[T2]v // ERROR "invalid map key"
|
||||||
|
_ map[T3]v // ERROR "invalid map key"
|
||||||
|
_ map[T4]v // ERROR "invalid map key"
|
||||||
|
_ map[T5]v
|
||||||
|
_ map[T6]v
|
||||||
|
_ map[T7]v
|
||||||
|
_ map[T8]v
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type T1 []int
|
||||||
|
type T2 struct { F T1 }
|
||||||
|
type T3 []T4
|
||||||
|
type T4 struct { F T3 }
|
||||||
|
|
||||||
|
type T5 *int
|
||||||
|
type T6 struct { F T5 }
|
||||||
|
type T7 *T4
|
||||||
|
type T8 struct { F *T7 }
|
||||||
|
Loading…
Reference in New Issue
Block a user