hasdefer = 0;
walk(curfn);
- if(nerrors != 0)
+ if(nerrors != 0 || isblank(curfn->nname))
goto ret;
allocparams();
hasdefer = 0;
walk(curfn);
- if(nerrors != 0)
+ if(nerrors != 0 || isblank(curfn->nname))
goto ret;
allocparams();
hasdefer = 0;
walk(curfn);
- if(nerrors != 0)
+ if(nerrors != 0 || isblank(curfn->nname))
goto ret;
allocparams();
w = 0;
switch(et) {
default:
- fatal("dowidth: unknown type: %E", t->etype);
+ fatal("dowidth: unknown type: %T", t);
break;
/* compiler-specific stuff */
int gen;
static int typegen, vargen;
+ if(isblank(n))
+ return;
+
s = n->sym;
gen = 0;
if(ctxt == PEXTERN) {
int doexpr;
Node *v, *e;
NodeList *init;
- Sym *s;
init = nil;
doexpr = el != nil;
e = N;
v = vl->n;
- s = v->sym;
-
v->op = ONAME;
declare(v, dclcontext);
v->ntype = t;
nnew = 0;
for(l=left; l; l=l->next) {
n = l->n;
+ if(isblank(n))
+ continue;
if(!colasname(n)) {
yyerror("non-name %#N on left side of :=", n);
continue;
f->sym = f->nname->sym;
if(pkgimportname != S && !exportname(f->sym->name))
f->sym = pkglookup(f->sym->name, structpkg);
- if(f->sym) {
+ if(f->sym && !isblank(f->nname)) {
for(t1=*t0; t1!=T; t1=t1->down) {
if(t1->sym == f->sym) {
yyerror("duplicate field %s", t1->sym->name);
t = n;
n = N;
}
+ if(isblank(n))
+ n = N;
if(n != N && n->sym == S) {
t = n;
n = N;
cgen_as(n->heapaddr, n->alloc);
}
+/*
+ * generate discard of value
+ */
+void
+cgen_discard(Node *nr)
+{
+ Node tmp;
+
+ if(nr == N)
+ return;
+
+ switch(nr->op) {
+ case ONAME:
+ break;
+
+ // unary
+ case OADD:
+ case OAND:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLSH:
+ case OLT:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case OOR:
+ case ORSH:
+ case OSUB:
+ case OXOR:
+ cgen_discard(nr->left);
+ cgen_discard(nr->right);
+ break;
+
+ // binary
+ case OCAP:
+ case OCOM:
+ case OLEN:
+ case OMINUS:
+ case ONOT:
+ case OPLUS:
+ cgen_discard(nr->left);
+ break;
+
+ // special enough to just evaluate
+ default:
+ tempname(&tmp, nr->type);
+ cgen_as(&tmp, nr);
+ gused(&tmp);
+ }
+}
+
/*
* generate assignment:
* nl = nr
dump("cgen_as = ", nr);
}
+ if(isblank(nl)) {
+ cgen_discard(nr);
+ return;
+ }
+
iszer = 0;
if(nr == N || isnil(nr)) {
// externals and heaps should already be clear
#include <libc.h>
#include <bio.h>
+// avoid <ctype.h>
+#undef isblank
+#define isblank goisblank
+
#ifndef EXTERN
#define EXTERN extern
#endif
// pseudo-types for literals
TIDEAL,
TNIL,
+ TBLANK,
NTYPE,
};
Ecall = 1<<4, // call-only expressions are ok
Efnstruct = 1<<5, // multivalue function returns are ok
Eiota = 1<<6, // iota is ok
+ Easgn = 1<<7, // assigning to expression
};
#define BITS 5
int isnilinter(Type*);
int isddd(Type*);
int isideal(Type*);
+int isblank(Node*);
Type* maptype(Type*, Type*);
Type* methtype(Type*);
Node* typename(Type*);
// (the type of x in const x = "hello").
// TODO(rsc): this may need some more thought.
idealstring = typ(TSTRING);
+
+ s = lookup("_");
+ s->block = -100;
+ s->def = nod(ONAME, N, N);
+ s->def->sym = s;
+ types[TBLANK] = typ(TBLANK);
+ s->def->type = types[TBLANK];
}
struct
int32 h;
char *p;
+ if(strcmp(pkg, "_") == 0)
+ yyerror("invalid package name _");
+
if(package == nopackage) {
// redefine all names to be this package.
for(h=0; h<NHASH; h++)
// delicate little dance. see typecheckas2
for(ll=n->list; ll; ll=ll->next)
if(ll->n->defn != n)
- typecheck(&ll->n, Erv);
+ typecheck(&ll->n, Erv | Easgn);
typecheck(&n->right, Erv);
if((t = n->right->type) == T)
case TARRAY:
hv1 = nod(OXXX, N, n);
- tempname(hv1, v1->type);
+ tempname(hv1, types[TINT]);
init = list(init, nod(OAS, hv1, N));
n->ntest = nod(OLT, hv1, nod(OLEN, ha, N));
case TCHAN:
hv1 = nod(OXXX, N, n);
- tempname(hv1, v1->type);
+ tempname(hv1, t->type);
n->ntest = nod(ONOT, nod(OCLOSED, ha, N), N);
n->ntest->ninit = list1(nod(OAS, hv1, nod(ORECV, ha, N)));
[TSTRING] = "string",
[TNIL] = "nil",
[TIDEAL] = "ideal",
+ [TBLANK] = "blank",
};
int
return t != T && t->etype == TARRAY && t->bound < 0;
}
+int
+isblank(Node *n)
+{
+ char *p;
+
+ if(n == N || n->sym == S)
+ return 0;
+ p = n->sym->name;
+ if(p == nil)
+ return 0;
+ return p[0] == '_' && p[1] == '\0';
+}
+
int
isselect(Node *n)
{
n = *np;
if(n == N)
return N;
- if(n->typecheck == 1 && n->op != ONAME) // XXX for test/func4.go
+
+ // Skip typecheck if already done.
+ // But re-typecheck ONAME node in case context has changed.
+ if(n->typecheck == 1 && n->op != ONAME)
return n;
if(n->typecheck == 2)
fatal("typecheck loop");
*/
case OLITERAL:
ok |= Erv;
- if(n->iota && !(top & Eiota))
+ if(n->iota && !(top & Eiota)) {
yyerror("use of iota not in constant initializer");
+ goto error;
+ }
if(n->val.ctype == CTSTR)
n->type = idealstring;
goto ret;
ok |= Ecall;
goto ret;
}
+ if(isblank(n) && !(top & Easgn)) {
+ yyerror("cannot use _ as value");
+ goto error;
+ }
ok |= Erv;
goto ret;
}
yyerror("cannot slice %#N (type %T)", l, t);
goto error;
-
+
/*
* call and call like
*/
case TFORW:
case TIDEAL:
case TNIL:
+ case TBLANK:
break;
default:
checkwidth(t);
*/
if(nt == T)
return 0;
+
+ if(t->etype == TBLANK) {
+ *op = OCONVNOP;
+ return 0;
+ }
if(eqtype(t, nt)) {
exportassignok(t);
// 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);
+ typecheck(&n->left, Erv | Easgn);
checkassign(n->left);
typecheck(&n->right, Erv);
// just to get it over with. see dance above.
n->typecheck = 1;
if(n->left->typecheck == 0)
- typecheck(&n->left, Erv);
+ typecheck(&n->left, Erv | Easgn);
}
static void
for(ll=n->list; ll; ll=ll->next) {
// delicate little dance.
if(ll->n->defn != n || ll->n->ntype)
- typecheck(&ll->n, Erv);
+ typecheck(&ll->n, Erv | Easgn);
}
cl = count(n->list);
cr = count(n->rlist);
{
Type *t, *rcvr;
- typecheck(&n->nname, Erv);
+ typecheck(&n->nname, Erv | Easgn);
if((t = n->nname->type) == T)
return;
n->type = t;
}
/*
- * n is an lv and t is the type of an rv
+ * l is an lv and rt is the type of an rv
* return 1 if this implies a function call
* evaluating the lv or a function call
* in the conversion of the types
if(r == T)
break;
l = ll->n;
+ if(isblank(l)) {
+ r = structnext(&saver);
+ continue;
+ }
// any lv that causes a fn call must be
// deferred until all the return arguments
--- /dev/null
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// Copyright 2009 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.
+
+package main
+
+var call string
+
+type T struct {
+ _, _, _ int;
+}
+
+const (
+ c0 = iota;
+ _;
+ _;
+ _;
+ c4;
+)
+
+var ints = []string {
+ "1",
+ "2",
+ "3"
+}
+
+func f() (int, int) {
+ call += "f";
+ return 1,2
+}
+
+func g() (float, float) {
+ call += "g";
+ return 3,4
+}
+
+func h(_ int, _ float) {
+}
+
+func i() int {
+ call += "i";
+ return 23;
+}
+
+func main()
+{
+ _, _ = f();
+ a, _ := f();
+ if a != 1 {panic(a)}
+ b, _ := g();
+ if b != 3 {panic(b)}
+ _, a = f();
+ if a != 2 {panic(a)}
+ _, b = g();
+ if b != 4 {panic(b)}
+ _ = i();
+ if call != "ffgfgi" {panic(call)}
+ if c4 != 4 {panic(c4)}
+
+ out := "";
+ for _, s := range ints {
+ out += s;
+ }
+ if out != "123" {panic(out)}
+
+ sum := 0;
+ for s, _ := range ints {
+ sum += s;
+ }
+ if sum != 3 {panic(sum)}
+
+ h(a,b);
+}
+
+// useless but legal
+var _ int = 1;
+var _ = 2;
+var _, _ = 3, 4;
+const _ = 3;
+const _, _ = 4, 5;
+type _ int;
+func _() {
+ panic("oops")
+}
+
+func ff() {
+ var _ int = 1;
+}
--- /dev/null
+// errchk $G -e $D/$F.go
+
+// Copyright 2009 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.
+
+package _ // ERROR "invalid package name _"
+
+func main() {
+ _(); // ERROR "cannot use _ as value"
+ x := _+1; // ERROR "cannot use _ as value"
+}
func f1() string {
const f = 3.141592;
- _ := float64(float32(f)); // appears to change the precision of f
+ x := float64(float32(f)); // appears to change the precision of f
return fmt.Sprintf("%v", float64(f));
}