Commit a1b5cb1d authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: simplify isGoConst

The only ways to construct an OLITERAL node are (1) a basic literal
from the source package, (2) constant folding within evconst (which
only folds Go language constants), (3) the universal "nil" constant,
and (4) implicit conversions of nil to some concrete type.

Passes toolstash-check.

Change-Id: I30fc6b07ebede7adbdfa4ed562436cbb7078a2ff
Reviewed-on: https://go-review.googlesource.com/c/go/+/166981
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 3def15cd
...@@ -584,14 +584,6 @@ func Isconst(n *Node, ct Ctype) bool { ...@@ -584,14 +584,6 @@ func Isconst(n *Node, ct Ctype) bool {
// evconst rewrites constant expressions into OLITERAL nodes. // evconst rewrites constant expressions into OLITERAL nodes.
func evconst(n *Node) { func evconst(n *Node) {
if !n.isGoConst() {
// Avoid constant evaluation of things that aren't actually constants
// according to the spec. See issue 24760.
// The SSA backend has a more robust optimizer that will catch
// all of these weird cases (like uintptr(unsafe.Pointer(uintptr(1)))).
return
}
nl, nr := n.Left, n.Right nl, nr := n.Left, n.Right
// Pick off just the opcodes that can be constant evaluated. // Pick off just the opcodes that can be constant evaluated.
...@@ -626,24 +618,18 @@ func evconst(n *Node) { ...@@ -626,24 +618,18 @@ func evconst(n *Node) {
} }
case OCONV: case OCONV:
if n.Type != nil && okforconst[n.Type.Etype] && nl.Op == OLITERAL { if okforconst[n.Type.Etype] && nl.Op == OLITERAL {
// TODO(mdempsky): There should be a convval function. // TODO(mdempsky): There should be a convval function.
setconst(n, convlit1(nl, n.Type, true, false).Val()) setconst(n, convlit1(nl, n.Type, true, false).Val())
} }
case OCONVNOP: case OCONVNOP:
if nl.Op == OLITERAL && nl.isGoConst() { if okforconst[n.Type.Etype] && nl.Op == OLITERAL {
// set so n.Orig gets OCONV instead of OCONVNOP // set so n.Orig gets OCONV instead of OCONVNOP
n.Op = OCONV n.Op = OCONV
setconst(n, nl.Val()) setconst(n, nl.Val())
} }
case OBYTES2STR:
// string([]byte(nil)) or string([]rune(nil))
if nl.Op == OLITERAL && nl.Val().Ctype() == CTNIL {
setconst(n, Val{U: ""})
}
case OADDSTR: case OADDSTR:
// Merge adjacent constants in the argument list. // Merge adjacent constants in the argument list.
s := n.List.Slice() s := n.List.Slice()
...@@ -657,6 +643,17 @@ func evconst(n *Node) { ...@@ -657,6 +643,17 @@ func evconst(n *Node) {
i2++ i2++
} }
// Hack to appease toolstash. Because
// we were checking isGoConst early
// on, we wouldn't collapse adjacent
// string constants unless the entire
// string was a constant.
//
// TODO(mdempsky): Remove in next commit.
if i1 != 0 || i2 != len(s) {
return
}
nl := *s[i1] nl := *s[i1]
nl.Orig = &nl nl.Orig = &nl
nl.SetVal(Val{strings.Join(strs, "")}) nl.SetVal(Val{strings.Join(strs, "")})
...@@ -714,6 +711,10 @@ func evconst(n *Node) { ...@@ -714,6 +711,10 @@ func evconst(n *Node) {
} }
case OCOMPLEX: case OCOMPLEX:
if nl == nil || nr == nil {
// TODO(mdempsky): Remove after early OAS2FUNC rewrite CL lands.
break
}
if nl.Op == OLITERAL && nr.Op == OLITERAL { if nl.Op == OLITERAL && nr.Op == OLITERAL {
// make it a complex literal // make it a complex literal
c := newMpcmplx() c := newMpcmplx()
...@@ -1338,88 +1339,7 @@ func indexconst(n *Node) int64 { ...@@ -1338,88 +1339,7 @@ func indexconst(n *Node) int64 {
// Expressions derived from nil, like string([]byte(nil)), while they // Expressions derived from nil, like string([]byte(nil)), while they
// may be known at compile time, are not Go language constants. // may be known at compile time, are not Go language constants.
func (n *Node) isGoConst() bool { func (n *Node) isGoConst() bool {
if n.Orig != nil { return n.Op == OLITERAL && n.Val().Ctype() != CTNIL
n = n.Orig
}
switch n.Op {
case OADD,
OAND,
OANDAND,
OANDNOT,
OBITNOT,
ODIV,
OEQ,
OGE,
OGT,
OLE,
OLSH,
OLT,
ONEG,
OMOD,
OMUL,
ONE,
ONOT,
OOR,
OOROR,
OPLUS,
ORSH,
OSUB,
OXOR,
OIOTA,
OREAL,
OIMAG:
if n.Left.isGoConst() && (n.Right == nil || n.Right.isGoConst()) {
return true
}
case OCOMPLEX:
if n.List.Len() == 0 && n.Left.isGoConst() && n.Right.isGoConst() {
return true
}
case OADDSTR:
for _, n1 := range n.List.Slice() {
if !n1.isGoConst() {
return false
}
}
return true
case OCONV, OCONVNOP:
if okforconst[n.Type.Etype] && n.Left.isGoConst() {
return true
}
case OLEN, OCAP:
l := n.Left
if l.isGoConst() {
return true
}
// 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 != nil && t.IsPtr() {
t = t.Elem()
}
if t != nil && t.IsArray() && !hascallchan(l) {
return true
}
case OLITERAL:
if n.Val().Ctype() != CTNIL {
return true
}
case OALIGNOF, OOFFSETOF, OSIZEOF:
return true
}
//dump("nonconst", n);
return false
} }
func hascallchan(n *Node) bool { func hascallchan(n *Node) bool {
......
...@@ -3671,17 +3671,18 @@ func typecheckdef(n *Node) { ...@@ -3671,17 +3671,18 @@ func typecheckdef(n *Node) {
} }
e = typecheck(e, ctxExpr) e = typecheck(e, ctxExpr)
if Isconst(e, CTNIL) { if e.Type == nil {
yyerrorl(n.Pos, "const initializer cannot be nil")
goto ret goto ret
} }
if !e.isGoConst() {
if e.Type != nil && e.Op != OLITERAL || !e.isGoConst() {
if !e.Diag() { if !e.Diag() {
if Isconst(e, CTNIL) {
yyerrorl(n.Pos, "const initializer cannot be nil")
} else {
yyerrorl(n.Pos, "const initializer %v is not a constant", e) yyerrorl(n.Pos, "const initializer %v is not a constant", e)
}
e.SetDiag(true) e.SetDiag(true)
} }
goto ret goto ret
} }
......
...@@ -11,5 +11,5 @@ package main ...@@ -11,5 +11,5 @@ package main
type ByteSize float64 type ByteSize float64
const ( const (
_ = iota; // ignore first value by assigning to blank identifier _ = iota; // ignore first value by assigning to blank identifier
KB ByteSize = 1<<(10*X) // ERROR "undefined" "is not a constant|as type ByteSize" KB ByteSize = 1<<(10*X) // ERROR "undefined"
) )
...@@ -8,4 +8,4 @@ package a ...@@ -8,4 +8,4 @@ package a
import "fmt" // ERROR "imported and not used" import "fmt" // ERROR "imported and not used"
const n = fmt // ERROR "fmt without selector" "fmt is not a constant" const n = fmt // ERROR "fmt without selector"
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
package main package main
const A = complex(0()) // ERROR "cannot call non-function" "const initializer .* is not a constant" const A = complex(0()) // ERROR "cannot call non-function"
...@@ -18,6 +18,6 @@ const ( ...@@ -18,6 +18,6 @@ const (
const ( const (
c = len([1 - iota]int{}) c = len([1 - iota]int{})
d d
e // ERROR "array bound must be non-negative" "const initializer len\(composite literal\) is not a constant" e // ERROR "array bound must be non-negative"
f // ERROR "array bound must be non-negative" "const initializer len\(composite literal\) is not a constant" f // ERROR "array bound must be non-negative"
) )
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment