diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index 78c676346fa..83be82f92fe 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -438,20 +438,6 @@ newtype(Sym *s) return t; } -/* - * type check top level declarations - */ -void -dclchecks(void) -{ - NodeList *l; - - for(l=externdcl; l; l=l->next) { - if(l->n->op != ONAME) - continue; - typecheck(&l->n, Erv); - } -} /* * := declarations diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index f5c0443f84b..d379a0d88a9 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -862,7 +862,6 @@ NodeList* checkarglist(NodeList *all, int input); Node* colas(NodeList *left, NodeList *right); void colasdefn(NodeList *left, Node *defn); NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); -void dclchecks(void); Node* dclname(Sym *s); void declare(Node *n, int ctxt); Type* dostruct(NodeList *l, int et); @@ -1166,6 +1165,11 @@ int exportassignok(Type *t, char *desc); int islvalue(Node *n); Node* typecheck(Node **np, int top); void typechecklist(NodeList *l, int top); +Node* typecheckdef(Node *n); +void resumetypecopy(void); +void copytype(Node *n, Type *t); +void defertypecopy(Node *n, Type *t); +void queuemethod(Node *n); /* * unsafe.c @@ -1177,15 +1181,10 @@ Node* unsafenmagic(Node *n); */ Node* callnew(Type *t); Node* chanfn(char *name, int n, Type *t); -void copytype(Node *n, Type *t); -void defertypecopy(Node *n, Type *t); Node* mkcall(char *name, Type *t, NodeList **init, ...); Node* mkcall1(Node *fn, Type *t, NodeList **init, ...); -void queuemethod(Node *n); -void resumetypecopy(void); int vmatch1(Node *l, Node *r); void walk(Node *fn); -Node* walkdef(Node *n); void walkexpr(Node **np, NodeList **init); void walkexprlist(NodeList *l, NodeList **init); void walkexprlistsafe(NodeList *l, NodeList **init); diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 5e2f73fc5ab..0c36e53a353 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -274,7 +274,9 @@ main(int argc, char *argv[]) funccompile(l->n, 1); } - dclchecks(); + for(l=externdcl; l; l=l->next) + if(l->n->op == ONAME) + typecheck(&l->n, Erv); if(nerrors) errorexit(); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 44d08352da7..04dc1a5077d 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -8,7 +8,6 @@ * evaluates compile time constants. * marks variables that escape the local frame. * rewrites n->op to be more specific in some cases. - * sets n->walk to walking function. */ #include "go.h" @@ -33,6 +32,8 @@ static void stringtoarraylit(Node**); static Node* resolve(Node*); static Type* getforwtype(Node*); +static NodeList* typecheckdefstack; + /* * resolve ONONAME to definition, if any. */ @@ -159,7 +160,7 @@ typecheck(Node **np, int top) if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T) defertypecopy(n, ft); - walkdef(n); + typecheckdef(n); n->realtype = n->type; if(n->op == ONONAME) goto error; @@ -2523,3 +2524,294 @@ getforwtype(Node *n) } } } + +static int ntypecheckdeftype; +static NodeList *methodqueue; + +static void +domethod(Node *n) +{ + Node *nt; + + nt = n->type->nname; + typecheck(&nt, Etype); + if(nt->type == T) { + // type check failed; leave empty func + n->type->etype = TFUNC; + n->type->nod = N; + return; + } + *n->type = *nt->type; + n->type->nod = N; + checkwidth(n->type); +} + +typedef struct NodeTypeList NodeTypeList; +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 +copytype(Node *n, Type *t) +{ + *n->type = *t; + + t = n->type; + t->sym = n->sym; + t->local = n->local; + t->vargen = n->vargen; + t->siggen = 0; + t->method = nil; + t->nod = N; + t->printed = 0; + t->deferwidth = 0; +} + +static void +typecheckdeftype(Node *n) +{ + int maplineno, embedlineno, lno; + Type *t; + NodeList *l; + + ntypecheckdeftype++; + lno = lineno; + setlineno(n); + n->type->sym = n->sym; + n->typecheck = 1; + typecheck(&n->ntype, Etype); + if((t = n->ntype->type) == T) { + n->diag = 1; + goto ret; + } + if(n->type == T) { + n->diag = 1; + goto ret; + } + + maplineno = n->type->maplineno; + embedlineno = n->type->embedlineno; + + // copy new type and clear fields + // that don't come along. + // anything zeroed here must be zeroed in + // typedcl2 too. + 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: + lineno = lno; + + // if there are no type definitions going on, it's safe to + // try to resolve the method types for the interfaces + // we just read. + if(ntypecheckdeftype == 1) { + while((l = methodqueue) != nil) { + methodqueue = nil; + for(; l; l=l->next) + domethod(l->n); + } + } + ntypecheckdeftype--; +} + +void +queuemethod(Node *n) +{ + if(ntypecheckdeftype == 0) { + domethod(n); + return; + } + methodqueue = list(methodqueue, n); +} + +Node* +typecheckdef(Node *n) +{ + int lno; + Node *e; + Type *t; + NodeList *l; + + lno = lineno; + setlineno(n); + + if(n->op == ONONAME) { + if(!n->diag) { + n->diag = 1; + if(n->lineno != 0) + lineno = n->lineno; + yyerror("undefined: %S", n->sym); + } + return n; + } + + if(n->walkdef == 1) + return n; + + l = mal(sizeof *l); + l->n = n; + l->next = typecheckdefstack; + typecheckdefstack = l; + + if(n->walkdef == 2) { + flusherrors(); + print("typecheckdef loop:"); + for(l=typecheckdefstack; l; l=l->next) + print(" %S", l->n->sym); + print("\n"); + fatal("typecheckdef loop"); + } + n->walkdef = 2; + + if(n->type != T || n->sym == S) // builtin or no name + goto ret; + + switch(n->op) { + default: + fatal("typecheckdef %O", n->op); + + case OLITERAL: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + n->ntype = N; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + e = n->defn; + n->defn = N; + if(e == N) { + lineno = n->lineno; + dump("typecheckdef nil defn", n); + yyerror("xxx"); + } + typecheck(&e, Erv | Eiota); + if(e->type != T && e->op != OLITERAL) { + yyerror("const initializer must be constant"); + goto ret; + } + if(isconst(e, CTNIL)) { + yyerror("const initializer cannot be nil"); + goto ret; + } + t = n->type; + if(t != T) { + if(!okforconst[t->etype]) { + yyerror("invalid constant type %T", t); + goto ret; + } + if(!isideal(e->type) && !eqtype(t, e->type)) { + yyerror("cannot use %+N as type %T in const initializer", e, t); + goto ret; + } + convlit(&e, t); + } + n->val = e->val; + n->type = e->type; + break; + + case ONAME: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + if(n->type != T) + break; + if(n->defn == N) { + if(n->etype != 0) // like OPRINTN + break; + if(nerrors > 0) { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break; + } + fatal("var without type, init: %S", n->sym); + } + if(n->defn->op == ONAME) { + typecheck(&n->defn, Erv); + n->type = n->defn->type; + break; + } + typecheck(&n->defn, Etop); // fills in n->type + break; + + case OTYPE: + if(curfn) + defercheckwidth(); + n->walkdef = 1; + n->type = typ(TFORW); + n->type->sym = n->sym; + typecheckdeftype(n); + if(curfn) + resumecheckwidth(); + break; + + case OPACK: + // nothing to see here + break; + } + +ret: + if(typecheckdefstack->n != n) + fatal("typecheckdefstack mismatch"); + l = typecheckdefstack; + typecheckdefstack = l->next; + + lineno = lno; + n->walkdef = 1; + return n; +} diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index ccc65ff2158..65a504bff5f 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,8 +21,6 @@ static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); -static NodeList* walkdefstack; - // can this code branch reach the end // without an unconditional RETURN // this is hard, so it is conservative @@ -100,296 +98,6 @@ walk(Node *fn) } } -static int nwalkdeftype; -static NodeList *methodqueue; - -static void -domethod(Node *n) -{ - Node *nt; - - nt = n->type->nname; - typecheck(&nt, Etype); - if(nt->type == T) { - // type check failed; leave empty func - n->type->etype = TFUNC; - n->type->nod = N; - return; - } - *n->type = *nt->type; - n->type->nod = N; - checkwidth(n->type); -} - -typedef struct NodeTypeList NodeTypeList; -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 -copytype(Node *n, Type *t) -{ - *n->type = *t; - - t = n->type; - t->sym = n->sym; - t->local = n->local; - t->vargen = n->vargen; - t->siggen = 0; - t->method = nil; - t->nod = N; - t->printed = 0; - t->deferwidth = 0; -} - -static void -walkdeftype(Node *n) -{ - int maplineno, embedlineno, lno; - Type *t; - NodeList *l; - - nwalkdeftype++; - lno = lineno; - setlineno(n); - n->type->sym = n->sym; - n->typecheck = 1; - typecheck(&n->ntype, Etype); - if((t = n->ntype->type) == T) { - n->diag = 1; - goto ret; - } - if(n->type == T) { - n->diag = 1; - goto ret; - } - - maplineno = n->type->maplineno; - embedlineno = n->type->embedlineno; - - // copy new type and clear fields - // that don't come along. - // anything zeroed here must be zeroed in - // typedcl2 too. - 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: - lineno = lno; - - // if there are no type definitions going on, it's safe to - // try to resolve the method types for the interfaces - // we just read. - if(nwalkdeftype == 1) { - while((l = methodqueue) != nil) { - methodqueue = nil; - for(; l; l=l->next) - domethod(l->n); - } - } - nwalkdeftype--; -} - -void -queuemethod(Node *n) -{ - if(nwalkdeftype == 0) { - domethod(n); - return; - } - methodqueue = list(methodqueue, n); -} - -Node* -walkdef(Node *n) -{ - int lno; - Node *e; - Type *t; - NodeList *l; - - lno = lineno; - setlineno(n); - - if(n->op == ONONAME) { - if(!n->diag) { - n->diag = 1; - if(n->lineno != 0) - lineno = n->lineno; - yyerror("undefined: %S", n->sym); - } - return n; - } - - if(n->walkdef == 1) - return n; - - l = mal(sizeof *l); - l->n = n; - l->next = walkdefstack; - walkdefstack = l; - - if(n->walkdef == 2) { - flusherrors(); - print("walkdef loop:"); - for(l=walkdefstack; l; l=l->next) - print(" %S", l->n->sym); - print("\n"); - fatal("walkdef loop"); - } - n->walkdef = 2; - - if(n->type != T || n->sym == S) // builtin or no name - goto ret; - - switch(n->op) { - default: - fatal("walkdef %O", n->op); - - case OLITERAL: - if(n->ntype != N) { - typecheck(&n->ntype, Etype); - n->type = n->ntype->type; - n->ntype = N; - if(n->type == T) { - n->diag = 1; - goto ret; - } - } - e = n->defn; - n->defn = N; - if(e == N) { - lineno = n->lineno; - dump("walkdef nil defn", n); - yyerror("xxx"); - } - typecheck(&e, Erv | Eiota); - if(e->type != T && e->op != OLITERAL) { - yyerror("const initializer must be constant"); - goto ret; - } - if(isconst(e, CTNIL)) { - yyerror("const initializer cannot be nil"); - goto ret; - } - t = n->type; - if(t != T) { - if(!okforconst[t->etype]) { - yyerror("invalid constant type %T", t); - goto ret; - } - if(!isideal(e->type) && !eqtype(t, e->type)) { - yyerror("cannot use %+N as type %T in const initializer", e, t); - goto ret; - } - convlit(&e, t); - } - n->val = e->val; - n->type = e->type; - break; - - case ONAME: - if(n->ntype != N) { - typecheck(&n->ntype, Etype); - n->type = n->ntype->type; - if(n->type == T) { - n->diag = 1; - goto ret; - } - } - if(n->type != T) - break; - if(n->defn == N) { - if(n->etype != 0) // like OPRINTN - break; - if(nerrors > 0) { - // Can have undefined variables in x := foo - // that make x have an n->ndefn == nil. - // If there are other errors anyway, don't - // bother adding to the noise. - break; - } - fatal("var without type, init: %S", n->sym); - } - if(n->defn->op == ONAME) { - typecheck(&n->defn, Erv); - n->type = n->defn->type; - break; - } - typecheck(&n->defn, Etop); // fills in n->type - break; - - case OTYPE: - if(curfn) - defercheckwidth(); - n->walkdef = 1; - n->type = typ(TFORW); - n->type->sym = n->sym; - walkdeftype(n); - if(curfn) - resumecheckwidth(); - break; - - case OPACK: - // nothing to see here - break; - } - -ret: - if(walkdefstack->n != n) - fatal("walkdefstack mismatch"); - l = walkdefstack; - walkdefstack = l->next; - - lineno = lno; - n->walkdef = 1; - return n; -} void walkstmtlist(NodeList *l)