]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/gc: reject non-Go constants
authorRuss Cox <rsc@golang.org>
Sat, 2 Feb 2013 04:10:02 +0000 (23:10 -0500)
committerRuss Cox <rsc@golang.org>
Sat, 2 Feb 2013 04:10:02 +0000 (23:10 -0500)
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 #4673.
Fixes #4680.

R=ken2
CC=golang-dev
https://golang.org/cl/7278043

src/cmd/gc/const.c
src/cmd/gc/go.h
src/cmd/gc/subr.c
src/cmd/gc/typecheck.c
test/const1.go
test/const5.go
test/fixedbugs/bug297.go
test/fixedbugs/issue4097.go
test/fixedbugs/issue4654.go
test/run.go

index f82ba9420d10599dacff75c680b2db968766ef45..83e62bde1d24d26e7fc80df1b5af89e8fa33c19f 100644 (file)
@@ -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;
+}
index 9d2ff4d466e6a797b21d45e87b215c7b8e468696..1f8446bd39dcd412449978f50a7de64019cd0f80 100644 (file)
@@ -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);
index afbdd0ccadbbd1b297c26799f325e186932524c0..01e738bf9d60cb0aa23e4b046be91c68a5f29c35 100644 (file)
@@ -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);
index 3771613af8878ac6c1e371643a008b6d3779b56b..1bfa0cc471818fbdd16ee20f580740ddb94fd7a5 100644 (file)
@@ -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]) {
index 1580b76c68374f1582171ddc5246cf68be890953..a170ce9e763dc30c4efcb16d646bc1afe53a87d0 100644 (file)
@@ -9,6 +9,8 @@
 
 package main
 
+import "unsafe"
+
 type I interface{}
 
 const (
@@ -86,3 +88,7 @@ func main() {
 }
 
 const ptr = nil // ERROR "const.*nil"
+const _ = string([]byte(nil)) // ERROR "is not a constant"
+const _ = uintptr(unsafe.Pointer((*int)(nil))) // ERROR "is not a constant"
+const _ = unsafe.Pointer((*int)(nil)) // ERROR "cannot be nil"
+const _ = (*int)(nil) // ERROR "cannot be nil"
index d0eed137d1ab7567d41246b8a616f0fb9fde2fc5..87fe33a385515317ea9f0e59314ec3c7870d4155 100644 (file)
@@ -24,10 +24,10 @@ const (
        n2 = len(m[""])
        n3 = len(s[10])
 
-       n4 = len(f())  // ERROR "must be constant|is not constant"
-       n5 = len(<-c) // ERROR "must be constant|is not constant"
+       n4 = len(f())  // ERROR "is not a constant|is not constant"
+       n5 = len(<-c) // ERROR "is not a constant|is not constant"
 
-       n6 = cap(f())  // ERROR "must be constant|is not constant"
-       n7 = cap(<-c) // ERROR "must be constant|is not constant"
+       n6 = cap(f())  // ERROR "is not a constant|is not constant"
+       n7 = cap(<-c) // ERROR "is not a constant|is not constant"
 )
 
index b5dfa8d878e196fc6391bc93caba01a160876f10..ee2ff92437d3a41b42bb1a0e205901f59e29e621 100644 (file)
@@ -11,5 +11,5 @@ package main
 type ByteSize float64
 const (
        _ = iota;   // ignore first value by assigning to blank identifier
-       KB ByteSize = 1<<(10*X) // ERROR "undefined" "as type ByteSize"
+       KB ByteSize = 1<<(10*X) // ERROR "undefined" "is not a constant|as type ByteSize"
 )
index 2c999a8336e6b2d8e66954ba801efcff62d396f1..fa942c9db74acc972318d9138e60540a7c9b15cb 100644 (file)
@@ -7,5 +7,5 @@
 package foo
 
 var s [][10]int
-const m = len(s[len(s)-1]) // ERROR "must be constant" 
+const m = len(s[len(s)-1]) // ERROR "is not a constant" 
 
index 4c5a55cb437f825b27afe5b61aef655022ccb1fd..170594e4b860a5f78706ba8ac5b870ce7f59c0f2 100644 (file)
@@ -48,7 +48,7 @@ func f() {
        defer recover() // ok
 
        int(0) // ERROR "int\(0\) evaluated but not used"
-       string([]byte("abc")) // ERROR "string\(\[\]byte literal\) evaluated but not used"
+       string([]byte("abc")) // ERROR "string\(.*\) evaluated but not used"
 
        append(x, 1) // ERROR "not used"
        cap(x) // ERROR "not used"
index bc545df10ba6534b48eed006ce6ffaca0f39c841..36c8b7ad7bbab37598431cf9a0ee43c937cf54f8 100644 (file)
@@ -683,6 +683,7 @@ func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
                        continue
                }
                matched := false
+               n := len(out)
                for _, errmsg := range errmsgs {
                        if we.re.MatchString(errmsg) {
                                matched = true
@@ -691,7 +692,7 @@ func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
                        }
                }
                if !matched {
-                       errs = append(errs, fmt.Errorf("%s:%d: no match for %q in%s", we.file, we.lineNum, we.reStr, strings.Join(out, "\n")))
+                       errs = append(errs, fmt.Errorf("%s:%d: no match for %#q in:\n\t%s", we.file, we.lineNum, we.reStr, strings.Join(out[n:], "\n\t")))
                        continue
                }
        }
@@ -758,7 +759,7 @@ func (t *test) wantedErrors(file, short string) (errs []wantedError) {
                all := m[1]
                mm := errQuotesRx.FindAllStringSubmatch(all, -1)
                if mm == nil {
-                       log.Fatalf("invalid errchk line in %s: %s", t.goFileName(), line)
+                       log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line)
                }
                for _, m := range mm {
                        rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
@@ -772,10 +773,14 @@ func (t *test) wantedErrors(file, short string) (errs []wantedError) {
                                }
                                return fmt.Sprintf("%s:%d", short, n)
                        })
-                       filterPattern := fmt.Sprintf(`^(\w+/)?%s:%d[:[]`, short, lineNum)
+                       re, err := regexp.Compile(rx)
+                       if err != nil {
+                               log.Fatalf("%s:%d: invalid regexp in ERROR line: %v", t.goFileName(), lineNum, err)
+                       }
+                       filterPattern := fmt.Sprintf(`^(\w+/)?%s:%d[:[]`, regexp.QuoteMeta(short), lineNum)
                        errs = append(errs, wantedError{
                                reStr:    rx,
-                               re:       regexp.MustCompile(rx),
+                               re:       re,
                                filterRe: regexp.MustCompile(filterPattern),
                                lineNum:  lineNum,
                                file:     short,