summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2013-02-01 23:10:02 -0500
committerRuss Cox <rsc@golang.org>2013-02-01 23:10:02 -0500
commit1688d2d3509547436f2e6900efee3b23d18773d8 (patch)
tree3ca83bae682c1177f1d4d9cae6370210cf354cef /src
parentd8d17d345bee871b8c550e4d51164b86fcd53a3d (diff)
downloadgo-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.c134
-rw-r--r--src/cmd/gc/go.h1
-rw-r--r--src/cmd/gc/subr.c1
-rw-r--r--src/cmd/gc/typecheck.c11
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]) {