Skip to content

Commit bd60de5

Browse files
authored
interp: allow early constant evaluation from builtin call
One builtin has been identified to be used for constant definition: len(), with a constant string argument. Add support for this. Fixes #1012.
1 parent 274eecd commit bd60de5

File tree

5 files changed

+72
-5
lines changed

5 files changed

+72
-5
lines changed

_test/const23.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package main
2+
3+
const maxlen = len("hello")
4+
5+
var gfm = [maxlen]byte{}
6+
7+
func main() {
8+
println(len(gfm))
9+
}
10+
11+
// Output:
12+
// 5

_test/const24.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package main
2+
3+
var aa = [...]int{1, 2, 3}
4+
5+
const maxlen = cap(aa)
6+
7+
var gfm = [maxlen]byte{}
8+
9+
func main() {
10+
println(len(gfm))
11+
}
12+
13+
// Output:
14+
// 3

interp/cfg.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
565565
switch {
566566
case n.action != aAssign:
567567
// Do not optimize assign combined with another operator.
568+
case src.rval.IsValid():
569+
// Do not skip assign operation when setting from a constant value.
568570
case isMapEntry(dest):
569571
// Setting a map entry needs an additional step, do not optimize.
570572
// As we only write, skip the default useless getIndexMap dest action.
@@ -855,13 +857,15 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
855857
wireChild(n)
856858
switch {
857859
case interp.isBuiltinCall(n):
858-
err = check.builtin(n.child[0].ident, n, n.child[1:], n.action == aCallSlice)
860+
c0 := n.child[0]
861+
bname := c0.ident
862+
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
859863
if err != nil {
860864
break
861865
}
862866

863-
n.gen = n.child[0].sym.builtin
864-
n.child[0].typ = &itype{cat: builtinT}
867+
n.gen = c0.sym.builtin
868+
c0.typ = &itype{cat: builtinT}
865869
if n.typ, err = nodeType(interp, sc, n); err != nil {
866870
return
867871
}
@@ -872,10 +876,18 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
872876
case n.anc.kind == returnStmt:
873877
// Store result directly to frame output location, to avoid a frame copy.
874878
n.findex = 0
879+
case bname == "cap" && isInConstOrTypeDecl(n):
880+
capConst(n)
881+
n.findex = notInFrame
882+
n.gen = nop
883+
case bname == "len" && isInConstOrTypeDecl(n):
884+
lenConst(n)
885+
n.findex = notInFrame
886+
n.gen = nop
875887
default:
876888
n.findex = sc.add(n.typ)
877889
}
878-
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
890+
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
879891
op(n) // pre-compute non-assigned constant :
880892
}
881893
case n.child[0].isType(sc):
@@ -2326,6 +2338,20 @@ func isRecursiveField(n *node) bool {
23262338
return false
23272339
}
23282340

2341+
func isInConstOrTypeDecl(n *node) bool {
2342+
anc := n.anc
2343+
for anc != nil {
2344+
switch anc.kind {
2345+
case constDecl, typeDecl:
2346+
return true
2347+
case varDecl, funcDecl:
2348+
return false
2349+
}
2350+
anc = anc.anc
2351+
}
2352+
return false
2353+
}
2354+
23292355
// isNewDefine returns true if node refers to a new definition.
23302356
func isNewDefine(n *node, sc *scope) bool {
23312357
if n.ident == "_" {

interp/run.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,21 @@ func _delete(n *node) {
28472847
})
28482848
}
28492849

2850+
func capConst(n *node) {
2851+
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
2852+
// There is no Cap() method for reflect.Type, just return Len() instead.
2853+
n.rval.SetInt(int64(n.child[1].typ.TypeOf().Len()))
2854+
}
2855+
2856+
func lenConst(n *node) {
2857+
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
2858+
if c1 := n.child[1]; c1.rval.IsValid() {
2859+
n.rval.SetInt(int64(len(vString(c1.rval))))
2860+
} else {
2861+
n.rval.SetInt(int64(c1.typ.TypeOf().Len()))
2862+
}
2863+
}
2864+
28502865
func _len(n *node) {
28512866
dest := genValueOutput(n, reflect.TypeOf(int(0)))
28522867
value := genValue(n.child[1])

interp/type.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
198198
if sym.kind != constSym {
199199
return nil, c0.cfgErrorf("non-constant array bound %q", c0.ident)
200200
}
201-
if sym.typ == nil || sym.typ.cat != intT {
201+
if sym.typ == nil || sym.typ.cat != intT || !sym.rval.IsValid() {
202202
t.incomplete = true
203203
break
204204
}

0 commit comments

Comments
 (0)