diff options
author | Russ Cox <rsc@golang.org> | 2013-02-01 23:10:02 -0500 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2013-02-01 23:10:02 -0500 |
commit | 1688d2d3509547436f2e6900efee3b23d18773d8 (patch) | |
tree | 3ca83bae682c1177f1d4d9cae6370210cf354cef /src | |
parent | d8d17d345bee871b8c550e4d51164b86fcd53a3d (diff) | |
download | go-1688d2d3509547436f2e6900efee3b23d18773d8.tar.gz |
cmd/gc: reject non-Go constants
Expressions involving nil, even if they can be evaluated
at compile time, do not count as Go constants and cannot
be used in const initializers.
Fixes issue 4673.
Fixes issue 4680.
R=ken2
CC=golang-dev
https://codereview.appspot.com/7278043
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd/gc/const.c | 134 | ||||
-rw-r--r-- | src/cmd/gc/go.h | 1 | ||||
-rw-r--r-- | src/cmd/gc/subr.c | 1 | ||||
-rw-r--r-- | src/cmd/gc/typecheck.c | 11 |
4 files changed, 139 insertions, 8 deletions
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index f82ba9420..83e62bde1 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -78,6 +78,7 @@ convlit1(Node **np, Type *t, int explicit) if(!explicit && !isideal(n->type)) return; + if(n->op == OLITERAL) { nn = nod(OXXX, N, N); *nn = *n; @@ -953,10 +954,6 @@ ret: *n = *nl; // restore value of n->orig. n->orig = norig; - if(norig->op == OCONV) { - dump("N", n); - dump("NORIG", norig); - } n->val = v; // check range. @@ -1449,3 +1446,132 @@ cmplxdiv(Mpcplx *v, Mpcplx *rv) mpsubfltflt(&v->imag, &ad); // bc-ad mpdivfltflt(&v->imag, &cc_plus_dd); // (bc+ad)/(cc+dd) } + +static int hascallchan(Node*); + +// Is n a Go language constant (as opposed to a compile-time constant)? +// Expressions derived from nil, like string([]byte(nil)), while they +// may be known at compile time, are not Go language constants. +// Only called for expressions known to evaluated to compile-time +// constants. +int +isgoconst(Node *n) +{ + Node *l; + Type *t; + + if(n->orig != N) + n = n->orig; + + switch(n->op) { + case OADD: + case OADDSTR: + case OAND: + case OANDAND: + case OANDNOT: + case OCOM: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLSH: + case OLT: + case OMINUS: + case OMOD: + case OMUL: + case ONE: + case ONOT: + case OOR: + case OOROR: + case OPLUS: + case ORSH: + case OSUB: + case OXOR: + case OCONV: + case OIOTA: + case OCOMPLEX: + case OREAL: + case OIMAG: + if(isgoconst(n->left) && (n->right == N || isgoconst(n->right))) + return 1; + break; + + case OLEN: + case OCAP: + l = n->left; + if(isgoconst(l)) + return 1; + // Special case: len/cap is constant when applied to array or + // pointer to array when the expression does not contain + // function calls or channel receive operations. + t = l->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t) && !hascallchan(l)) + return 1; + break; + + case OLITERAL: + if(n->val.ctype != CTNIL) + return 1; + break; + + case ONAME: + l = n->sym->def; + if(l->op == OLITERAL && n->val.ctype != CTNIL) + return 1; + break; + + case ONONAME: + if(n->sym->def != N && n->sym->def->op == OIOTA) + return 1; + break; + + case OCALL: + // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. + l = n->left; + while(l->op == OPAREN) + l = l->left; + if(l->op != ONAME || l->sym->pkg != unsafepkg) + break; + if(strcmp(l->sym->name, "Alignof") == 0 || + strcmp(l->sym->name, "Offsetof") == 0 || + strcmp(l->sym->name, "Sizeof") == 0) + return 1; + break; + } + + //dump("nonconst", n); + return 0; +} + +static int +hascallchan(Node *n) +{ + NodeList *l; + + if(n == N) + return 0; + switch(n->op) { + case OCALL: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + case ORECV: + return 1; + } + + if(hascallchan(n->left) || + hascallchan(n->right)) + return 1; + + for(l=n->list; l; l=l->next) + if(hascallchan(l->n)) + return 1; + for(l=n->rlist; l; l=l->next) + if(hascallchan(l->n)) + return 1; + + return 0; +} diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 9d2ff4d46..1f8446bd3 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -997,6 +997,7 @@ void defaultlit(Node **np, Type *t); void defaultlit2(Node **lp, Node **rp, int force); void evconst(Node *n); int isconst(Node *n, int ct); +int isgoconst(Node *n); Node* nodcplxlit(Val r, Val i); Node* nodlit(Val v); long nonnegconst(Node *n); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index afbdd0cca..01e738bf9 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -840,6 +840,7 @@ treecopy(Node *n) default: m = nod(OXXX, N, N); *m = *n; + m->orig = m; m->left = treecopy(n->left); m->right = treecopy(n->right); m->list = listtreecopy(n->list); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 3771613af..1bfa0cc47 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -1336,6 +1336,9 @@ reswitch: case OCONV: doconv: ok |= Erv; + l = nod(OXXX, N, N); + n->orig = l; + *l = *n; typecheck(&n->left, Erv | (top & (Eindir | Eiota))); convlit1(&n->left, n->type, 1); if((t = n->left->type) == T || n->type == T) @@ -3007,14 +3010,14 @@ typecheckdef(Node *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; } + if(e->type != T && e->op != OLITERAL || !isgoconst(e)) { + yyerror("const initializer %N is not a constant", e); + goto ret; + } t = n->type; if(t != T) { if(!okforconst[t->etype]) { |