CC=quietgcc
LD=quietgcc
-CFLAGS=-ggdb -I$(GOROOT)/include -O1
+CFLAGS=-ggdb -I$(GOROOT)/include -O1 -fno-inline
O=o
YFLAGS=-d
# GNU Make syntax:
}
}
+/*
+ * := declarations
+ */
+
+static int
+colasname(Node *n)
+{
+ // TODO(rsc): can probably simplify
+ // once late-binding of names goes in
+ switch(n->op) {
+ case ONAME:
+ case ONONAME:
+ case OPACK:
+ case OTYPE:
+ case OLITERAL:
+ return n->sym != S;
+ }
+ return 0;
+}
+
+Node*
+old2new(Node *n, Type *t, NodeList **init)
+{
+ Node *l;
+
+ if(!colasname(n)) {
+ yyerror("left side of := must be a name");
+ return n;
+ }
+ if(t != T && t->funarg) {
+ yyerror("use of multi func value as single value in :=");
+ return n;
+ }
+ l = newname(n->sym);
+ dodclvar(l, t, init);
+ return l;
+}
+
+Node*
+colas(NodeList *left, NodeList *right)
+{
+ int nnew;
+ Node *n, *as;
+ NodeList *l;
+
+ if(count(left) == 1 && count(right) == 1)
+ as = nod(OAS, left->n, right->n);
+ else {
+ as = nod(OAS2, N, N);
+ as->list = left;
+ as->rlist = right;
+ }
+ as->colas = 1;
+
+ nnew = 0;
+ for(l=left; l; l=l->next) {
+ n = l->n;
+ if(!colasname(n)) {
+ yyerror("non-name %#N on left side of :=", n);
+ continue;
+ }
+ if(n->sym->block == block)
+ continue;
+ nnew++;
+ n = newname(n->sym);
+ declare(n, dclcontext);
+ if(as->op == OAS)
+ as->left = n;
+ n->defn = as;
+ as->ninit = list(as->ninit, nod(ODCL, n, N));
+ l->n = n;
+ }
+ if(nnew == 0)
+ yyerror("no new variables on left side of :=");
+ return as;
+}
+
/*
* structs, functions, and methods.
n = l->n;
if(n->op != ONAME || n->class != PAUTO)
continue;
- typecheck(&n, Erv);
+ typecheck(&n, Erv); // only needed for unused variables
+ if(n->type == T)
+ continue;
dowidth(n->type);
w = n->type->width;
if(n->class & PHEAP)
Etop = 1<<1, // evaluated at statement level
Erv = 1<<2, // evaluated in value context
Etype = 1<<3,
- Ecall = 1<<4,
+ Ecall = 1<<4, // call-only expressions are ok
+ Efnstruct = 1<<5, // multivalue function returns are ok
};
#define BITS 5
// done in casebody()
poptodcl();
$$ = nod(OXCASE, N, N);
- typecheck(&$4, Erv);
- $$->list = list1(nod(OAS, selectas($2, $4, &$$->ninit), $4));
+// $$->list = list1(nod(OAS, selectas($2, $4, &$$->ninit), $4));
+ $$->list = list1(colas(list1($2), list1($4)));
}
| LDEFAULT ':'
{
ncase->list = nil;
setlineno(n);
switch(n->op) {
+ default:
+ yyerror("select case must be receive, send or assign recv");;
+ break;
+
case OAS:
// convert x = <-c into OSELRECV(x, c)
if(n->right->op != ORECV) {
r = nod(OIF, N, N);
r->nbody = ncase->ninit;
ncase->ninit = nil;
+ if(n != nil) {
+ r->nbody = concat(r->nbody, n->ninit);
+ n->ninit = nil;
+ }
if(n == nil) {
// selectdefault(sel *byte);
r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
print("%O-ntype\n", n->op);
dodump(n->ntype, dep+1);
}
- if(n->defn != nil) {
+ if(n->defn != nil && n->defn->op != OAS && n->defn->op != OAS2) {
indent(dep);
print("%O-defn\n", n->op);
dodump(n->defn, dep+1);
static void typecheckcomplit(Node**);
static void addrescapes(Node*);
static void typecheckas2(Node*);
+static void typecheckas(Node*);
static void checklvalue(Node*, char*);
static void checkassign(Node*);
static void checkassignlist(NodeList*);
case ONAME:
if(n->etype != 0) {
- yyerror("must call builtin %S", n->sym);
- goto error;
+ ok |= Ecall;
+ goto ret;
}
ok |= Erv;
goto ret;
* exprs
*/
case OADDR:
+ ok |= Erv;
typecheck(&n->left, Erv);
if(n->left->type == T)
goto error;
goto ret;
case OCOMPLIT:
+ ok |= Erv;
typecheckcomplit(&n);
if(n->type == T)
goto error;
goto ret;
case ODOTTYPE:
+ ok |= Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
l = n->left;
goto ret;
case OINDEX:
+ ok |= Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
implicitstar(&n->left);
goto error;
case TARRAY:
- ok |= Erv;
defaultlit(&n->right, types[TUINT]);
n->type = t->type;
break;
case TMAP:
n->etype = 0;
- ok |= Erv;
defaultlit(&n->right, t->down);
n->type = t->type;
n->op = OINDEXMAP;
break;
case TSTRING:
- ok |= Erv;
defaultlit(&n->right, types[TUINT]);
n->type = types[TUINT8];
n->op = OINDEXSTR;
goto ret;
case ORECV:
+ ok |= Etop | Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
l = n->left;
goto error;
}
n->type = t->type;
- ok |= Erv;
goto ret;
case OSEND:
+ ok |= Etop | Erv;
l = typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
defaultlit(&n->left, T);
n->etype = 0;
if(top & Erv)
n->op = OSENDNB;
- ok |= Etop | Erv;
n->type = types[TBOOL];
goto ret;
typecheck(&n->left, Erv | Etype | Ecall);
defaultlit(&n->left, T);
l = n->left;
- typechecklist(n->list, Erv);
+ if(count(n->list) == 1)
+ typecheck(&n->list->n, Erv | Efnstruct);
+ else
+ typechecklist(n->list, Erv);
if((t = l->type) == T)
goto error;
dowidth(t);
break;
}
typecheckaste(OCALL, getinargx(t), n->list);
- if(t->outtuple == 0) {
- ok |= Etop;
+ ok |= Etop;
+ if(t->outtuple == 0)
goto ret;
- }
+ ok |= Erv;
if(t->outtuple == 1) {
- ok |= Erv;
t = getoutargx(l->type)->type;
if(t->etype == TFIELD)
t = t->type;
goto ret;
}
// multiple return
- // ok |= Emulti;
+ if(!(top & (Efnstruct | Etop))) {
+ yyerror("multiple-value %#N() in single-value context", l);
+ goto ret;
+ }
n->type = getoutargx(l->type);
goto ret;
case OCAP:
case OLEN:
+ ok |= Erv;
if(onearg(n) < 0)
goto error;
typecheck(&n->left, Erv);
case OCONV:
doconv:
+ ok |= Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, n->type);
if((t = n->left->type) == T || n->type == T)
goto ret;
case OMAKE:
+ ok |= Erv;
args = n->list;
if(args == nil) {
yyerror("missing argument to make");
goto ret;
case ONEW:
+ ok |= Erv;
args = n->list;
if(args == nil) {
yyerror("missing argument to new");
case OPANICN:
case OPRINT:
case OPRINTN:
+ ok |= Etop;
typechecklist(n->list, Erv);
goto ret;
* statements
*/
case OAS:
- typecheck(&n->left, Erv);
- checkassign(n->left);
- typecheck(&n->right, Erv);
- if(n->left->type != T && n->right && n->right->type != T)
- n->right = typecheckconv(nil, n->right, n->left->type, 0);
+ ok |= Etop;
+ typecheckas(n);
goto ret;
case OAS2:
+ ok |= Etop;
typecheckas2(n);
goto ret;
case OGOTO:
case OLABEL:
case OXFALL:
+ ok |= Etop;
goto ret;
case ODEFER:
case OPROC:
+ ok |= Etop;
typecheck(&n->left, Etop);
goto ret;
case OFOR:
+ ok |= Etop;
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
goto ret;
case OIF:
+ ok |= Etop;
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
goto ret;
case ORETURN:
- typechecklist(n->list, Erv);
+ ok |= Etop;
+ typechecklist(n->list, Erv | Efnstruct);
if(curfn->type->outnamed && n->list == nil)
goto ret;
typecheckaste(ORETURN, getoutargx(curfn->type), n->list);
goto ret;
case OSELECT:
+ ok |= Etop;
typecheckselect(n);
goto ret;
case OSWITCH:
+ ok |= Etop;
typecheckswitch(n);
goto ret;
case OTYPECASE:
+ ok |= Etop | Erv;
typecheck(&n->left, Erv);
- ok |= Erv;
goto ret;
case OTYPESW:
+ ok |= Etop;
typecheck(&n->right, Erv);
goto ret;
case OXCASE:
+ ok |= Etop;
typechecklist(n->list, Erv);
typechecklist(n->nbody, Etop);
goto ret;
goto error;
}
if((ok & Ecall) && !(top & Ecall)) {
- yyerror("must call method %#N", n);
+ yyerror("must call %#N", n);
+ goto error;
+ }
+ if((top & (Ecall|Erv|Etype)) && !(ok & (Erv|Etype|Ecall))) {
+ yyerror("%#N used as value", n);
+ goto error;
+ }
+ if((top & Etop) && !(ok & Etop)) {
+ yyerror("%#N not used", n);
goto error;
}
}
/*
- * multiple assignment
+ * type check assignment.
+ * if this assignment is the definition of a var on the left side,
+ * fill in the var's type.
*/
+
+static void
+typecheckas(Node *n)
+{
+ // delicate little dance.
+ // the definition of n may refer to this assignment
+ // as its definition, in which case it will call typecheckas.
+ // in that case, do not call typecheck back, or it will cycle.
+ // if the variable has a type (ntype) then typechecking
+ // will not look at defn, so it is okay (and desirable,
+ // so that the conversion below happens).
+ if(n->left->defn != n || n->left->ntype)
+ typecheck(&n->left, Erv);
+
+ checkassign(n->left);
+ typecheck(&n->right, Erv);
+ if(n->left->type != T && n->right && n->right->type != T)
+ n->right = typecheckconv(nil, n->right, n->left->type, 0);
+ if(n->left->defn == n && n->left->ntype == N) {
+ defaultlit(&n->right, T);
+ n->left->type = n->right->type;
+ }
+
+ // second half of dance.
+ // now that right is done, typecheck the left
+ // just to get it over with. see dance above.
+ n->typecheck = 1;
+ if(n->left->typecheck == 0)
+ typecheck(&n->left, Erv);
+}
+
static void
typecheckas2(Node *n)
{
Iter s;
Type *t;
- typechecklist(n->list, Erv);
- checkassignlist(n->list);
- typechecklist(n->rlist, Erv);
-
+ for(ll=n->list; ll; ll=ll->next) {
+ // delicate little dance.
+ if(ll->n->defn != n || ll->n->ntype)
+ typecheck(&ll->n, Erv);
+ }
cl = count(n->list);
cr = count(n->rlist);
+ checkassignlist(n->list);
+ if(cl > 1 && cr == 1)
+ typecheck(&n->rlist->n, Erv | Efnstruct);
+ else
+ typechecklist(n->rlist, Erv);
if(cl == cr) {
// easy
- for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
+ for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) {
if(ll->n->type != T && lr->n->type != T)
lr->n = typecheckconv(nil, lr->n, ll->n->type, 0);
- return;
+ if(ll->n->defn == n && ll->n->ntype == N) {
+ defaultlit(&lr->n, T);
+ ll->n->type = lr->n->type;
+ }
+ }
+ goto out;
}
// m[i] = x, ok
if(cl == 1 && cr == 2 && l->op == OINDEXMAP) {
if(l->type == T)
- return;
+ goto out;
n->op = OAS2MAPW;
n->rlist->n = typecheckconv(nil, r, l->type->down, 0);
r = n->rlist->next->n;
n->rlist->next->n = typecheckconv(nil, r, types[TBOOL], 0);
- return;
+ goto out;
}
// x,y,z = f()
if(cr == 1) {
if(r->type == T)
- return;
+ goto out;
switch(r->op) {
case OCALLMETH:
case OCALLINTER:
if(ll->n->type != T)
if(checkconv(t->type, ll->n->type, 0, &op, &et) < 0)
yyerror("cannot assign type %T to %+N", t->type, ll->n);
+ if(ll->n->defn == n && ll->n->ntype == N)
+ ll->n->type = t->type;
t = structnext(&s);
}
- return;
+ goto out;
}
}
// x, ok = y
if(cl == 2 && cr == 1) {
if(r->type == T)
- return;
+ goto out;
switch(r->op) {
case OINDEXMAP:
n->op = OAS2MAPR;
common:
if(l->type != T && checkconv(r->type, l->type, 0, &op, &et) < 0)
yyerror("cannot assign %+N to %+N", r, l);
+ if(l->defn == n)
+ l->type = r->type;
l = n->list->next->n;
if(l->type != T && checkconv(types[TBOOL], l->type, 0, &op, &et) < 0)
yyerror("cannot assign bool value to %+N", l);
- return;
+ if(l->defn == n && l->ntype == N)
+ l->type = types[TBOOL];
+ goto out;
}
}
mismatch:
yyerror("assignment count mismatch: %d = %d", cl, cr);
+
+out:
+ // second half of dance
+ n->typecheck = 1;
+ for(ll=n->list; ll; ll=ll->next)
+ if(ll->n->typecheck == 0)
+ typecheck(&ll->n, Erv);
}
if(n->type != T || n->sym == S) // builtin or no name
goto ret;
-
init = nil;
switch(n->op) {
case OLITERAL:
n->diag = 1;
goto ret;
}
- n->ntype = N;
}
if(n->type != T)
break;
if(n->defn == N)
fatal("var without type, init: %S", n->sym);
- switch(n->defn->op) {
- default:
- fatal("walkdef name defn");
- case OAS:
- typecheck(&n->defn->right, Erv);
- defaultlit(&n->defn->right, T);
- if((t = n->defn->right->type) == T) {
- n->diag = 1;
- goto ret;
- }
- n->type = t;
- break;
- }
+ typecheck(&n->defn, Etop); // fills in n->type
break;
}
r = nod(OCALL, on, N);
r->list = args;
- typecheck(&r, Erv);
+ typecheck(&r, Erv | Efnstruct);
walkexpr(&r, init);
return r;
}
return n;
}
-int
-colasname(Node *n)
-{
- // TODO(rsc): can probably simplify
- // once late-binding of names goes in
- switch(n->op) {
- case ONAME:
- case ONONAME:
- case OPACK:
- break;
- case OTYPE:
- case OLITERAL:
- if(n->sym != S)
- break;
- // fallthrough
- default:
- return 0;
- }
- return 1;
-}
-
-Node*
-old2new(Node *n, Type *t, NodeList **init)
-{
- Node *l;
-
- if(!colasname(n)) {
- yyerror("left side of := must be a name");
- return n;
- }
- if(t != T && t->funarg) {
- yyerror("use of multi func value as single value in :=");
- return n;
- }
- l = newname(n->sym);
- dodclvar(l, t, init);
- return l;
-}
-
-static Node*
-mixedoldnew(Node *n, Type *t)
-{
- n = nod(OXXX, n, N);
- n->type = t;
- return n;
-}
-
-static NodeList*
-checkmixed(NodeList *nl, NodeList **init)
-{
- Node *a, *l;
- NodeList *ll, *n;
- Type *t;
- int ntot, nred;
-
- // first pass, check if it is a special
- // case of new and old declarations
-
- ntot = 0; // number assignments
- nred = 0; // number redeclarations
- for(ll=nl; ll; ll=ll->next) {
- l = ll->n;
- t = l->type;
- l = l->left;
-
- if(!colasname(l))
- goto allnew;
- if(l->sym->block == block)
- nred++;
- ntot++;
- }
-
- // test for special case
- // a) multi-assignment (ntot>1)
- // b) at least one redeclaration (red>0)
- // c) not all redeclarations (nred!=ntot)
- if(nred == 0 || ntot <= 1 || nred == ntot)
- goto allnew;
-
- n = nil;
- for(ll=nl; ll; ll=ll->next) {
- l = ll->n;
- t = l->type;
- l = l->left;
-
- a = l;
- if(l->sym->block != block)
- a = old2new(l, t, init);
-
- n = list(n, a);
- }
- return n;
-
-allnew:
- // same as original
- n = nil;
- for(ll=nl; ll; ll=ll->next) {
- l = ll->n;
- t = l->type;
- l = l->left;
-
- a = old2new(l, t, init);
- n = list(n, a);
- }
- return n;
-}
-
-Node*
-colas(NodeList *ll, NodeList *lr)
-{
- Node *l, *r, *a, *nl, *nr;
- Iter savet;
- NodeList *init, *savel, *saver, *n;
- Type *t;
- int cl, cr;
-
- /* nl is an expression list.
- * nr is an expression list.
- * return a newname-list from
- * types derived from the rhs.
- */
- cr = count(lr);
- cl = count(ll);
- init = nil;
- n = nil;
-
- /* check calls early, to give better message for a := f() */
- if(cr == 1) {
- nr = lr->n;
- switch(nr->op) {
- case OCALL:
- case OCALLFUNC:
- if(nr->left->op == ONAME && nr->left->etype != 0)
- break;
- typecheck(&nr->left, Erv | Etype | Ecall);
- walkexpr(&nr->left, &init);
- if(nr->left->op == OTYPE)
- break;
- goto call;
- case OCALLMETH:
- case OCALLINTER:
- typecheck(&nr->left, Erv);
- walkexpr(&nr->left, &init);
- call:
- convlit(&nr->left, types[TFUNC]);
- t = nr->left->type;
- if(t == T)
- goto outl; // error already printed
- if(t->etype == tptr)
- t = t->type;
- if(t == T || t->etype != TFUNC) {
- yyerror("cannot call %T", t);
- goto outl;
- }
- if(t->outtuple != cl) {
- cr = t->outtuple;
- goto badt;
- }
- // finish call - first half above
- t = structfirst(&savet, getoutarg(t));
- if(t == T)
- goto outl;
- for(savel=ll; savel; savel=savel->next) {
- l = savel->n;
- a = mixedoldnew(l, t->type);
- n = list(n, a);
- t = structnext(&savet);
- }
- n = checkmixed(n, &init);
- goto out;
- }
- }
- if(cl != cr) {
- if(cr == 1) {
- nr = lr->n;
- goto multi;
- }
- goto badt;
- }
-
- for(savel=ll, saver=lr; savel != nil; savel=savel->next, saver=saver->next) {
- l = savel->n;
- r = saver->n;
-
- typecheck(&r, Erv);
- defaultlit(&r, T);
- saver->n = r;
- a = mixedoldnew(l, r->type);
- n = list(n, a);
- }
- n = checkmixed(n, &init);
- goto out;
-
-multi:
- typecheck(&nr, Erv);
- lr->n = nr;
-
- /*
- * there is a list on the left
- * and a mono on the right.
- * go into the right to get
- * individual types for the left.
- */
- switch(nr->op) {
- default:
- goto badt;
-
- case OINDEXMAP:
- // check if rhs is a map index.
- // if so, types are valuetype,bool
- if(cl != 2)
- goto badt;
- walkexpr(&nr->left, &init);
- t = nr->left->type;
- a = mixedoldnew(ll->n, t->type);
- n = list1(a);
- a = mixedoldnew(ll->next->n, types[TBOOL]);
- n = list(n, a);
- n = checkmixed(n, &init);
- break;
-
- case ODOTTYPE:
- // a,b := i.(T)
- walkdottype(nr, &init);
- if(cl != 2)
- goto badt;
- // a,b = iface
- a = mixedoldnew(ll->n, nr->type);
- n = list1(a);
- a = mixedoldnew(ll->next->n, types[TBOOL]);
- n = list(n, a);
- n = checkmixed(n, &init);
- break;
-
- case ORECV:
- if(cl != 2)
- goto badt;
- walkexpr(&nr->left, &init);
- t = nr->left->type;
- if(!istype(t, TCHAN))
- goto badt;
- a = mixedoldnew(ll->n, t->type);
- n = list1(a);
- a = mixedoldnew(ll->next->n, types[TBOOL]);
- n = list(n, a);
- n = checkmixed(n, &init);
- break;
- }
- goto out;
-
-badt:
- nl = ll->n;
- if(nl->diag == 0) {
- nl->diag = 1;
- yyerror("assignment count mismatch: %d = %d %#N", cl, cr, lr->n);
- }
-outl:
- n = ll;
-
-out:
- // n is the lhs of the assignment.
- // init holds the list of declarations.
- a = nod(OAS2, N, N);
- a->list = n;
- a->rlist = lr;
- a->ninit = init;
- a->colas = 1;
- return a;
-}
-
/*
* rewrite a range statement
* k and v are names/new_names
r = nod(OCALL, fn, N);
r->list = args;
if(fn->type->outtuple > 0)
- typecheck(&r, Erv);
+ typecheck(&r, Erv | Efnstruct);
else
typecheck(&r, Etop);
walkexpr(&r, init);
do
(
xcd $i
- make clean
- time make
+ # make clean
+ # time make
+ make install
make test
) || exit $?
done
{
// simple redeclaration
i := f1();
- i := f1(); // ERROR "redeclared|redefinition"
+ i := f1(); // ERROR "redeclared|redefinition|no new"
}
{
// change of type for f
{
// no new variables
i, f, s := f3();
- i, f := f2(); // ERROR "redeclared|redefinition"
+ i, f := f2(); // ERROR "redeclared|redefinition|no new"
}
{
// single redeclaration
i, f, s := f3(); // GCCGO_ERROR "previous"
- i := f1(); // ERROR "redeclared|redefinition"
+ i := f1(); // ERROR "redeclared|redefinition|no new"
}
// double redeclaration
{
i, f, s := f3();
- i, f := f2(); // ERROR "redeclared|redefinition"
+ i, f := f2(); // ERROR "redeclared|redefinition|no new"
}
{
// triple redeclaration
i, f, s := f3();
- i, f, s := f3(); // ERROR "redeclared|redefinition"
+ i, f, s := f3(); // ERROR "redeclared|redefinition|no new"
}
}
func main() {
var x int;
- x := 0; // BUG: redeclaration - should not compile
+ x := 0; // ERROR "declar|:="
}
package main
func f9(a int) (i int, f float) {
- i := 9; // ERROR "redecl"
- f := float(9); // ERROR "redecl"
+ i := 9; // ERROR "redecl|no new"
+ f := float(9); // ERROR "redecl|no new"
return i, f;
}
func f() /* no return type */ {}
func main() {
- x := f(); // ERROR "mismatch"
+ x := f(); // ERROR "mismatch|as value"
}