type checking of assignments, switch, if, for

R=ken
OCL=32716
CL=32720
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 5f82b08..b8139c3 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -12,7 +12,8 @@
  *
  * TODO:
  *	trailing ... section of function calls
- *	statements
+ *	select
+ *	range
  */
 
 #include "go.h"
@@ -26,7 +27,7 @@
 static int	nokeys(NodeList*);
 static void	typecheckcomplit(Node**);
 static void	addrescapes(Node*);
-
+static void	typecheckas2(Node*);
 static void	checklvalue(Node*, char*);
 static void checkassign(Node*);
 static void checkassignlist(NodeList*);
@@ -815,9 +816,7 @@
 		goto ret;
 
 	case OAS2:
-		typechecklist(n->list, Erv);
-		checkassignlist(n->list);
-		typechecklist(n->rlist, Erv);
+		typecheckas2(n);
 		goto ret;
 
 	case OBREAK:
@@ -836,14 +835,18 @@
 
 	case OFOR:
 		typechecklist(n->ninit, Etop);
-		typecheck(&n->ntest, Erv);	// TODO Ebool
+		typecheck(&n->ntest, Erv);
+		if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
+			yyerror("non-bool %+N used as for condition");
 		typecheck(&n->nincr, Etop);
 		typechecklist(n->nbody, Etop);
 		goto ret;
 
 	case OIF:
 		typechecklist(n->ninit, Etop);
-		typecheck(&n->ntest, Erv);	// TODO Ebool
+		typecheck(&n->ntest, Erv);
+		if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
+			yyerror("non-bool %+N used as if condition");
 		typechecklist(n->nbody, Etop);
 		typechecklist(n->nelse, Etop);
 		goto ret;
@@ -862,13 +865,12 @@
 		goto ret;
 
 	case OSWITCH:
-		typechecklist(n->ninit, Etop);
-		typecheck(&n->ntest, Erv);
-		typechecklist(n->list, Etop);
+		typecheckswitch(n);
 		goto ret;
 
 	case OTYPECASE:
 		typecheck(&n->left, Erv);
+		ok |= Erv;
 		goto ret;
 
 	case OTYPESW:
@@ -888,7 +890,7 @@
 		goto error;
 	}
 	if((top & (Erv|Etype)) == Etype && n->op != OTYPE) {
-		yyerror("%O is not a type", n->op);
+		yyerror("%#N is not a type", n);
 		goto error;
 	}
 	if((ok & Ecall) && !(top & Ecall)) {
@@ -1043,183 +1045,163 @@
 	return 1;
 }
 
-Node*
-typecheckconv(Node *nconv, Node *n, Type *t, int explicit)
+static int
+checkconv(Type *nt, Type *t, int explicit, int *op, int *et)
 {
-	int et, op;
-	Node *n1;
-
-	op = OCONV;
-	et = 0;
+	*op = OCONV;
+	*et = 0;
 
 	// preexisting error
 	if(t == T || t->etype == TFORW)
-		return n;
+		return 0;
 
 	/*
 	 * implicit conversions
 	 */
+	if(nt == T)
+		return 0;
 
-	convlit1(&n, t, explicit);
-	if(n->type == T)
-		return n;
-
-	if(eqtype(t, n->type)) {
+	if(eqtype(t, nt)) {
 		exportassignok(t);
-		op = OCONVNOP;
-		if(!explicit || t == n->type)
-			return n;
-		goto conv;
+		*op = OCONVNOP;
+		if(!explicit || t == nt)
+			return 0;
+		return 1;
 	}
 
 	// interfaces are not subject to the name restrictions below.
-	// accept anything involving interfaces and let walkiface
+	// accept anything involving interfaces and let ifacecvt
 	// generate a good message.  some messages have to be
 	// delayed anyway.
-	if(isnilinter(t) || isnilinter(n->type) || isinter(t) || isinter(n->type)) {
-		et = ifaceas1(t, n->type, 0);
-		op = OCONVIFACE;
-		goto conv;
+	if(isnilinter(t) || isnilinter(nt) || isinter(t) || isinter(nt)) {
+		*et = ifaceas1(t, nt, 0);
+		*op = OCONVIFACE;
+		return 1;
 	}
 
 	// otherwise, if concrete types have names, they must match.
-	if(!explicit && t->sym && n->type->sym && t != n->type)
-		goto badimplicit;
+	if(!explicit && t->sym && nt->sym && t != nt)
+		return -1;
 
 	// channel must not lose directionality
-	if(t->etype == TCHAN && n->type->etype == TCHAN) {
-		if(t->chan & ~n->type->chan) {
-			if(!explicit)
-				goto badimplicit;
-			goto badexplicit;
-		}
-		if(eqtype(t->type, n->type->type)) {
-			op = OCONVNOP;
-			goto conv;
+	if(t->etype == TCHAN && nt->etype == TCHAN) {
+		if(t->chan & ~nt->chan)
+			return -1;
+		if(eqtype(t->type, nt->type)) {
+			*op = OCONVNOP;
+			return 1;
 		}
 	}
 
 	// array to slice
-	if(isslice(t) && isptr[n->type->etype] && isfixedarray(n->type->type)
-	&& eqtype(t->type, n->type->type->type)) {
-		op = OCONVSLICE;
-		goto conv;
-	}
-
-	if(!explicit) {
-	badimplicit:
-		yyerror("cannot use %+N as type %T", n, t);
-		n = nod(OCONV, n, N);	// leave type == T
-		n->typecheck = 1;
-		return n;
+	if(isslice(t) && isptr[nt->etype] && isfixedarray(nt->type)
+	&& eqtype(t->type, nt->type->type)) {
+		*op = OCONVSLICE;
+		return 1;
 	}
 
 	/*
 	 * explicit conversions
 	 */
+	if(!explicit)
+		return -1;
 
 	// same representation
-	if(cvttype(t, n->type)) {
-		if(n->op == OLITERAL) {
-			// can convert literal in place
-			n1 = nod(OXXX, N, N);
-			*n1 = *n;
-			n1->type = t;
-			return n1;
-		}
-		op = OCONVNOP;
-		goto conv;
+	if(cvttype(t, nt)) {
+		*op = OCONVNOP;
+		return 1;
 	}
 
 	// simple fix-float
 	if(isint[t->etype] || isfloat[t->etype])
-	if(isint[n->type->etype] || isfloat[n->type->etype]) {
-		// evconst(n);	// XXX is this needed?
-		goto conv;
-	}
+	if(isint[nt->etype] || isfloat[nt->etype])
+		return 1;
 
 	// to string
 	if(istype(t, TSTRING)) {
 		// integer rune
-		if(isint[n->type->etype]) {
-			op = ORUNESTR;
-			goto conv;
+		if(isint[nt->etype]) {
+			*op = ORUNESTR;
+			return 1;
 		}
 
-		// *[10]byte -> string?  convert *[10]byte -> []byte
+		// *[10]byte -> string
 		// in preparation for next step
-		if(isptr[n->type->etype] && isfixedarray(n->type->type)) {
-			switch(n->type->type->type->etype) {
+		if(isptr[nt->etype] && isfixedarray(nt->type)) {
+			switch(nt->type->type->etype) {
 			case TUINT8:
+				*op = OARRAYBYTESTR;
+				return 1;
 			case TINT:
-				n1 = nod(OCONV, n, N);
-				n1->type = typ(TARRAY);
-				n1->type->bound = -1;
-				n1->type->type = n->type->type->type;
-				dowidth(n1->type);
-				typecheck(&n1, Erv);
-				walkexpr(&n1, nil);
-				n = n1;
-				break;
+				*op = OARRAYRUNESTR;
+				return 1;
 			}
 		}
 
 		// []byte -> string
-		if(isslice(n->type)) {
-			switch(n->type->type->etype) {
+		if(isslice(nt)) {
+			switch(nt->type->etype) {
 			case TUINT8:
-				op = OARRAYBYTESTR;
-				goto conv;
+				*op = OARRAYBYTESTR;
+				return 1;
 			case TINT:
-				op = OARRAYRUNESTR;
-				goto conv;
+				*op = OARRAYRUNESTR;
+				return 1;
 			}
 		}
 	}
 
 	// convert to unsafe pointer
 	if(isptrto(t, TANY)
-	&& (isptr[n->type->etype] || n->type->etype == TUINTPTR))
-		goto conv;
+	&& (isptr[nt->etype] || nt->etype == TUINTPTR))
+		return 1;
 
 	// convert from unsafe pointer
-	if(isptrto(n->type, TANY)
+	if(isptrto(nt, TANY)
 	&& (isptr[t->etype] || t->etype == TUINTPTR))
-		goto conv;
+		return 1;
 
-badexplicit:
-	yyerror("cannot convert %+N to type %T", n, t);
-	nconv->type = T;
-	return nconv;
-
-conv:
-	if(nconv == nil) {
-		nconv = nod(OXXX, n, N);
-		nconv->type = t;
-		nconv->typecheck = 1;
-	}
-	nconv->etype = et;
-	nconv->op = op;
-	return nconv;
+	return -1;
 }
 
-/*
- * typecheck assignment: type list = type list
- */
-static void
-typecheckastt(int op, Type *t1, Type *t2)
+Node*
+typecheckconv(Node *nconv, Node *n, Type *t, int explicit)
 {
-	for(t1=t1->type, t2=t2->type; t1; t1=t1->down, t2=t2->down) {
-		if(t2 == nil) {
-			yyerror("too few");
-			return;
-		}
-		if(!eqtype(t1->type, t2->type)) {
-			yyerror("wrong");
-		}
+	int et, op;
+	Node *n1;
+
+	convlit1(&n, t, explicit);
+	if(n->type == T)
+		return n;
+
+	if(cvttype(t, n->type) && n->op == OLITERAL) {
+		// can convert literal in place
+		// TODO(rsc) is this needed?
+		n1 = nod(OXXX, N, N);
+		*n1 = *n;
+		n1->type = t;
+		return n1;
 	}
-	if(t2 != nil)
-		yyerror("too many");
+
+	switch(checkconv(n->type, t, explicit, &op, &et)) {
+	case -1:
+		if(explicit)
+			yyerror("cannot convert %+N to type %T", n, t);
+		else
+			yyerror("cannot use %+N as type %T", n, t);
+		return n;
+
+	case 0:
+		return n;
+	}
+
+	if(nconv == N)
+		nconv = nod(OCONV, n, N);
+	nconv->op = op;
+	nconv->etype = et;
+	nconv->type = t;
+	nconv->typecheck = 1;
+	return nconv;
 }
 
 /*
@@ -1228,34 +1210,57 @@
 static void
 typecheckaste(int op, Type *tstruct, NodeList *nl)
 {
-	Type *t, *tl;
+	Type *t, *tl, *tn;
 	Node *n;
+	int lno;
 
-	if(nl != nil && nl->next == nil && nl->n->type != T && nl->n->type->etype == TSTRUCT && nl->n->type->funarg) {
-		typecheckastt(op, tstruct, nl->n->type);
-		return;
+	lno = lineno;
+
+	if(nl != nil && nl->next == nil && (n = nl->n)->type != T)
+	if(n->type->etype == TSTRUCT && n->type->funarg) {
+		setlineno(n);
+		tn = n->type->type;
+		for(tl=tstruct->type; tl; tl=tl->down) {
+			int xx, yy;
+			if(tn == T) {
+				yyerror("not enough arguments to %#O", op);
+				goto out;
+			}
+			if(checkconv(tn->type, tl->type, 0, &xx, &yy) < 0)
+				yyerror("cannot use type %T as type %T", tn->type, tl->type);
+			tn = tn->down;
+		}
+		if(tn != T)
+			yyerror("too many arguments to %#O", op);
+		goto out;
 	}
 
 	for(tl=tstruct->type; tl; tl=tl->down) {
 		t = tl->type;
 		if(isddd(t)) {
-			for(; nl; nl=nl->next)
+			for(; nl; nl=nl->next) {
+				setlineno(nl->n);
 				defaultlit(&nl->n, T);
-			return;
+			}
+			goto out;
 		}
 		if(nl == nil) {
 			yyerror("not enough arguments to %#O", op);
-			return;
+			goto out;
 		}
 		n = nl->n;
+		setlineno(nl->n);
 		if(n->type != T)
 			nl->n = typecheckconv(nil, n, t, 0);
 		nl = nl->next;
 	}
 	if(nl != nil) {
 		yyerror("too many arguments to %#O", op);
-		return;
+		goto out;
 	}
+
+out:
+	lineno = lno;
 }
 
 /*
@@ -1612,6 +1617,9 @@
 	}
 }
 
+/*
+ * lvalue etc
+ */
 static int
 islvalue(Node *n)
 {
@@ -1655,3 +1663,98 @@
 	for(; l; l=l->next)
 		checkassign(l->n);
 }
+
+/*
+ * multiple assignment
+ */
+static void
+typecheckas2(Node *n)
+{
+	int cl, cr, op, et;
+	NodeList *ll, *lr;
+	Node *l, *r;
+	Iter s;
+	Type *t;
+
+	typechecklist(n->list, Erv);
+	checkassignlist(n->list);
+	typechecklist(n->rlist, Erv);
+
+	cl = count(n->list);
+	cr = count(n->rlist);
+
+	if(cl == cr) {
+		// easy
+		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;
+	}
+
+
+	l = n->list->n;
+	r = n->rlist->n;
+
+	// m[i] = x, ok
+	if(cl == 1 && cr == 2 && l->op == OINDEXMAP) {
+		if(l->type == T)
+			return;
+		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;
+	}
+
+	// x,y,z = f()
+	if(cr == 1) {
+		if(r->type == T)
+			return;
+		switch(r->op) {
+		case OCALLMETH:
+		case OCALLINTER:
+		case OCALLFUNC:
+			if(r->type->etype != TSTRUCT || r->type->funarg == 0)
+				break;
+			cr = structcount(r->type);
+			if(cr != cl)
+				goto mismatch;
+			n->op = OAS2FUNC;
+			t = structfirst(&s, &r->type);
+			for(ll=n->list; ll; ll=ll->next) {
+				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);
+				t = structnext(&s);
+			}
+			return;
+		}
+	}
+
+	// x, ok = y
+	if(cl == 2 && cr == 1) {
+		if(r->type == T)
+			return;
+		switch(r->op) {
+		case OINDEXMAP:
+			n->op = OAS2MAPR;
+			goto common;
+		case ORECV:
+			n->op = OAS2RECV;
+			goto common;
+		case ODOTTYPE:
+			n->op = OAS2DOTTYPE;
+		common:
+			if(l->type != T && checkconv(r->type, l->type, 0, &op, &et) < 0)
+				yyerror("cannot assign %+N to %+N", r, l);
+			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;
+		}
+	}
+
+mismatch:
+	yyerror("assignment count mismatch: %d = %d", cl, cr);
+}
+