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.
This commit is contained in:
12
_test/const23.go
Normal file
12
_test/const23.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const maxlen = len("hello")
|
||||||
|
|
||||||
|
var gfm = [maxlen]byte{}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println(len(gfm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 5
|
||||||
14
_test/const24.go
Normal file
14
_test/const24.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
var aa = [...]int{1, 2, 3}
|
||||||
|
|
||||||
|
const maxlen = cap(aa)
|
||||||
|
|
||||||
|
var gfm = [maxlen]byte{}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println(len(gfm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
@@ -565,6 +565,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
|||||||
switch {
|
switch {
|
||||||
case n.action != aAssign:
|
case n.action != aAssign:
|
||||||
// Do not optimize assign combined with another operator.
|
// Do not optimize assign combined with another operator.
|
||||||
|
case src.rval.IsValid():
|
||||||
|
// Do not skip assign operation when setting from a constant value.
|
||||||
case isMapEntry(dest):
|
case isMapEntry(dest):
|
||||||
// Setting a map entry needs an additional step, do not optimize.
|
// Setting a map entry needs an additional step, do not optimize.
|
||||||
// As we only write, skip the default useless getIndexMap dest action.
|
// 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) {
|
|||||||
wireChild(n)
|
wireChild(n)
|
||||||
switch {
|
switch {
|
||||||
case interp.isBuiltinCall(n):
|
case interp.isBuiltinCall(n):
|
||||||
err = check.builtin(n.child[0].ident, n, n.child[1:], n.action == aCallSlice)
|
c0 := n.child[0]
|
||||||
|
bname := c0.ident
|
||||||
|
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
n.gen = n.child[0].sym.builtin
|
n.gen = c0.sym.builtin
|
||||||
n.child[0].typ = &itype{cat: builtinT}
|
c0.typ = &itype{cat: builtinT}
|
||||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -872,10 +876,18 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
|||||||
case n.anc.kind == returnStmt:
|
case n.anc.kind == returnStmt:
|
||||||
// Store result directly to frame output location, to avoid a frame copy.
|
// Store result directly to frame output location, to avoid a frame copy.
|
||||||
n.findex = 0
|
n.findex = 0
|
||||||
|
case bname == "cap" && isInConstOrTypeDecl(n):
|
||||||
|
capConst(n)
|
||||||
|
n.findex = notInFrame
|
||||||
|
n.gen = nop
|
||||||
|
case bname == "len" && isInConstOrTypeDecl(n):
|
||||||
|
lenConst(n)
|
||||||
|
n.findex = notInFrame
|
||||||
|
n.gen = nop
|
||||||
default:
|
default:
|
||||||
n.findex = sc.add(n.typ)
|
n.findex = sc.add(n.typ)
|
||||||
}
|
}
|
||||||
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
|
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
|
||||||
op(n) // pre-compute non-assigned constant :
|
op(n) // pre-compute non-assigned constant :
|
||||||
}
|
}
|
||||||
case n.child[0].isType(sc):
|
case n.child[0].isType(sc):
|
||||||
@@ -2326,6 +2338,20 @@ func isRecursiveField(n *node) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInConstOrTypeDecl(n *node) bool {
|
||||||
|
anc := n.anc
|
||||||
|
for anc != nil {
|
||||||
|
switch anc.kind {
|
||||||
|
case constDecl, typeDecl:
|
||||||
|
return true
|
||||||
|
case varDecl, funcDecl:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
anc = anc.anc
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// isNewDefine returns true if node refers to a new definition.
|
// isNewDefine returns true if node refers to a new definition.
|
||||||
func isNewDefine(n *node, sc *scope) bool {
|
func isNewDefine(n *node, sc *scope) bool {
|
||||||
if n.ident == "_" {
|
if n.ident == "_" {
|
||||||
|
|||||||
@@ -2847,6 +2847,21 @@ func _delete(n *node) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func capConst(n *node) {
|
||||||
|
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
|
||||||
|
// There is no Cap() method for reflect.Type, just return Len() instead.
|
||||||
|
n.rval.SetInt(int64(n.child[1].typ.TypeOf().Len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func lenConst(n *node) {
|
||||||
|
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
|
||||||
|
if c1 := n.child[1]; c1.rval.IsValid() {
|
||||||
|
n.rval.SetInt(int64(len(vString(c1.rval))))
|
||||||
|
} else {
|
||||||
|
n.rval.SetInt(int64(c1.typ.TypeOf().Len()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func _len(n *node) {
|
func _len(n *node) {
|
||||||
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
||||||
value := genValue(n.child[1])
|
value := genValue(n.child[1])
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
|||||||
if sym.kind != constSym {
|
if sym.kind != constSym {
|
||||||
return nil, c0.cfgErrorf("non-constant array bound %q", c0.ident)
|
return nil, c0.cfgErrorf("non-constant array bound %q", c0.ident)
|
||||||
}
|
}
|
||||||
if sym.typ == nil || sym.typ.cat != intT {
|
if sym.typ == nil || sym.typ.cat != intT || !sym.rval.IsValid() {
|
||||||
t.incomplete = true
|
t.incomplete = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user