write-only variable _

R=ken
OCL=34465
CL=34470
This commit is contained in:
Russ Cox 2009-09-08 23:16:19 -07:00
parent c77c3b0196
commit 5438be4541
15 changed files with 235 additions and 20 deletions

View file

@ -48,7 +48,7 @@ compile(Node *fn)
hasdefer = 0;
walk(curfn);
if(nerrors != 0)
if(nerrors != 0 || isblank(curfn->nname))
goto ret;
allocparams();

View file

@ -49,7 +49,7 @@ compile(Node *fn)
hasdefer = 0;
walk(curfn);
if(nerrors != 0)
if(nerrors != 0 || isblank(curfn->nname))
goto ret;
allocparams();

View file

@ -47,7 +47,7 @@ compile(Node *fn)
hasdefer = 0;
walk(curfn);
if(nerrors != 0)
if(nerrors != 0 || isblank(curfn->nname))
goto ret;
allocparams();

View file

@ -139,7 +139,7 @@ dowidth(Type *t)
w = 0;
switch(et) {
default:
fatal("dowidth: unknown type: %E", t->etype);
fatal("dowidth: unknown type: %T", t);
break;
/* compiler-specific stuff */

View file

@ -164,6 +164,9 @@ declare(Node *n, int ctxt)
int gen;
static int typegen, vargen;
if(isblank(n))
return;
s = n->sym;
gen = 0;
if(ctxt == PEXTERN) {
@ -301,7 +304,6 @@ variter(NodeList *vl, Node *t, NodeList *el)
int doexpr;
Node *v, *e;
NodeList *init;
Sym *s;
init = nil;
doexpr = el != nil;
@ -317,8 +319,6 @@ variter(NodeList *vl, Node *t, NodeList *el)
e = N;
v = vl->n;
s = v->sym;
v->op = ONAME;
declare(v, dclcontext);
v->ntype = t;
@ -550,6 +550,8 @@ colasdefn(NodeList *left, Node *defn)
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;
@ -838,7 +840,7 @@ stotype(NodeList *l, int et, Type **t)
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);
@ -963,6 +965,8 @@ checkarglist(NodeList *all)
t = n;
n = N;
}
if(isblank(n))
n = N;
if(n != N && n->sym == S) {
t = n;
n = N;

View file

@ -430,6 +430,60 @@ cgen_dcl(Node *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
@ -450,6 +504,11 @@ cgen_as(Node *nl, Node *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

View file

@ -6,6 +6,10 @@
#include <libc.h>
#include <bio.h>
// avoid <ctype.h>
#undef isblank
#define isblank goisblank
#ifndef EXTERN
#define EXTERN extern
#endif
@ -421,6 +425,7 @@ enum
// pseudo-types for literals
TIDEAL,
TNIL,
TBLANK,
NTYPE,
};
@ -467,6 +472,7 @@ enum
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
@ -803,6 +809,7 @@ int isinter(Type*);
int isnilinter(Type*);
int isddd(Type*);
int isideal(Type*);
int isblank(Node*);
Type* maptype(Type*, Type*);
Type* methtype(Type*);
Node* typename(Type*);

View file

@ -1352,6 +1352,13 @@ lexinit(void)
// (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
@ -1431,6 +1438,9 @@ mkpackage(char* pkg)
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++)

View file

@ -19,7 +19,7 @@ typecheckrange(Node *n)
// 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)
@ -121,7 +121,7 @@ walkrange(Node *n)
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));
@ -169,7 +169,7 @@ walkrange(Node *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)));

View file

@ -953,6 +953,7 @@ basicnames[] =
[TSTRING] = "string",
[TNIL] = "nil",
[TIDEAL] = "ideal",
[TBLANK] = "blank",
};
int
@ -1421,6 +1422,19 @@ isslice(Type *t)
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)
{

View file

@ -61,7 +61,10 @@ typecheck(Node **np, int top)
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");
@ -85,8 +88,10 @@ reswitch:
*/
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;
@ -100,6 +105,10 @@ reswitch:
ok |= Ecall;
goto ret;
}
if(isblank(n) && !(top & Easgn)) {
yyerror("cannot use _ as value");
goto error;
}
ok |= Erv;
goto ret;
@ -581,7 +590,7 @@ reswitch:
}
yyerror("cannot slice %#N (type %T)", l, t);
goto error;
/*
* call and call like
*/
@ -970,6 +979,7 @@ ret:
case TFORW:
case TIDEAL:
case TNIL:
case TBLANK:
break;
default:
checkwidth(t);
@ -1165,6 +1175,11 @@ checkconv(Type *nt, Type *t, int explicit, int *op, int *et)
*/
if(nt == T)
return 0;
if(t->etype == TBLANK) {
*op = OCONVNOP;
return 0;
}
if(eqtype(t, nt)) {
exportassignok(t);
@ -1804,7 +1819,7 @@ typecheckas(Node *n)
// 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);
@ -1820,7 +1835,7 @@ typecheckas(Node *n)
// 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
@ -1835,7 +1850,7 @@ typecheckas2(Node *n)
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);
@ -1946,7 +1961,7 @@ typecheckfunc(Node *n)
{
Type *t, *rcvr;
typecheck(&n->nname, Erv);
typecheck(&n->nname, Erv | Easgn);
if((t = n->nname->type) == T)
return;
n->type = t;

View file

@ -1103,7 +1103,7 @@ ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
}
/*
* 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
@ -1141,6 +1141,10 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
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

90
test/blank.go Normal file
View file

@ -0,0 +1,90 @@
// $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;
}

12
test/blank1.go Normal file
View file

@ -0,0 +1,12 @@
// 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"
}

View file

@ -16,7 +16,7 @@ func f0() string {
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));
}