[dev.typeparams] cmd/compile: add and use ir.RawOrigExpr

This CL adds ir.RawOrigExpr, which can be used to represent arbitrary
constant expressions without needing to build and carry around an
entire IR representation of the original expression. It also allows
distinguishing how the constant was originally written by the
user (e.g., "0xff" vs "255").

This CL then also updates irgen to make use of this functionality for
expressions that were constant folded by types2.

Change-Id: I41e04e228e715ae2735c357b75633a2d08ee7021
Reviewed-on: https://go-review.googlesource.com/c/go/+/323210
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
This commit is contained in:
Matthew Dempsky 2021-05-27 02:50:17 -07:00
parent de5d1aca5e
commit ea522bc546
6 changed files with 107 additions and 7 deletions

View file

@ -448,6 +448,20 @@ func (n *ParenExpr) SetOTYPE(t *types.Type) {
t.SetNod(n) t.SetNod(n)
} }
// A RawOrigExpr represents an arbitrary Go expression as a string value.
// When printed in diagnostics, the string value is written out exactly as-is.
type RawOrigExpr struct {
miniExpr
Raw string
}
func NewRawOrigExpr(pos src.XPos, op Op, raw string) *RawOrigExpr {
n := &RawOrigExpr{Raw: raw}
n.pos = pos
n.op = op
return n
}
// A ResultExpr represents a direct access to a result. // A ResultExpr represents a direct access to a result.
type ResultExpr struct { type ResultExpr struct {
miniExpr miniExpr

View file

@ -567,6 +567,11 @@ func exprFmt(n Node, s fmt.State, prec int) {
return return
} }
if n, ok := n.(*RawOrigExpr); ok {
fmt.Fprint(s, n.Raw)
return
}
switch n.Op() { switch n.Op() {
case OPAREN: case OPAREN:
n := n.(*ParenExpr) n := n.(*ParenExpr)

View file

@ -947,6 +947,22 @@ func (n *RangeStmt) editChildren(edit func(Node) Node) {
} }
} }
func (n *RawOrigExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *RawOrigExpr) copy() Node {
c := *n
c.init = copyNodes(c.init)
return &c
}
func (n *RawOrigExpr) doChildren(do func(Node) bool) bool {
if doNodes(n.init, do) {
return true
}
return false
}
func (n *RawOrigExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *ResultExpr) copy() Node { func (n *ResultExpr) copy() Node {
c := *n c := *n

View file

@ -66,7 +66,7 @@ func Float64Val(v constant.Value) float64 {
func AssertValidTypeForConst(t *types.Type, v constant.Value) { func AssertValidTypeForConst(t *types.Type, v constant.Value) {
if !ValidTypeForConst(t, v) { if !ValidTypeForConst(t, v) {
base.Fatalf("%v does not represent %v (%v)", t, v, v.Kind()) base.Fatalf("%v (%v) does not represent %v (%v)", t, t.Kind(), v, v.Kind())
} }
} }

View file

@ -5,6 +5,8 @@
package noder package noder
import ( import (
"fmt"
"cmd/compile/internal/base" "cmd/compile/internal/base"
"cmd/compile/internal/ir" "cmd/compile/internal/ir"
"cmd/compile/internal/syntax" "cmd/compile/internal/syntax"
@ -15,6 +17,8 @@ import (
) )
func (g *irgen) expr(expr syntax.Expr) ir.Node { func (g *irgen) expr(expr syntax.Expr) ir.Node {
expr = unparen(expr) // skip parens; unneeded after parse+typecheck
if expr == nil { if expr == nil {
return nil return nil
} }
@ -67,7 +71,9 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
// Constant expression. // Constant expression.
if tv.Value != nil { if tv.Value != nil {
return Const(g.pos(expr), g.typ(typ), tv.Value) typ := g.typ(typ)
value := FixValue(typ, tv.Value)
return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr))
} }
n := g.expr0(typ, expr) n := g.expr0(typ, expr)
@ -161,9 +167,6 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
typed(g.typ(typ), n) typed(g.typ(typ), n)
return n return n
case *syntax.ParenExpr:
return g.expr(expr.X) // skip parens; unneeded after parse+typecheck
case *syntax.SelectorExpr: case *syntax.SelectorExpr:
// Qualified identifier. // Qualified identifier.
if name, ok := expr.X.(*syntax.Name); ok { if name, ok := expr.X.(*syntax.Name); ok {
@ -317,13 +320,17 @@ func getTargs(selinfo *types2.Selection) []types2.Type {
} }
func (g *irgen) exprList(expr syntax.Expr) []ir.Node { func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
return g.exprs(unpackListExpr(expr))
}
func unpackListExpr(expr syntax.Expr) []syntax.Expr {
switch expr := expr.(type) { switch expr := expr.(type) {
case nil: case nil:
return nil return nil
case *syntax.ListExpr: case *syntax.ListExpr:
return g.exprs(expr.ElemList) return expr.ElemList
default: default:
return []ir.Node{g.expr(expr)} return []syntax.Expr{expr}
} }
} }
@ -402,3 +409,35 @@ func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
} }
return n.Type() return n.Type()
} }
// constExprOp returns an ir.Op that represents the outermost
// operation of the given constant expression. It's intended for use
// with ir.RawOrigExpr.
func constExprOp(expr syntax.Expr) ir.Op {
switch expr := expr.(type) {
default:
panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr))
case *syntax.BasicLit:
return ir.OLITERAL
case *syntax.Name, *syntax.SelectorExpr:
return ir.ONAME
case *syntax.CallExpr:
return ir.OCALL
case *syntax.Operation:
if expr.Y == nil {
return unOps[expr.Op]
}
return binOps[expr.Op]
}
}
func unparen(expr syntax.Expr) syntax.Expr {
for {
paren, ok := expr.(*syntax.ParenExpr)
if !ok {
return expr
}
expr = paren.X
}
}

View file

@ -43,6 +43,32 @@ func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
return typed(typ, ir.NewBasicLit(pos, val)) return typed(typ, ir.NewBasicLit(pos, val))
} }
func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node {
orig := ir.NewRawOrigExpr(pos, op, raw)
return ir.NewConstExpr(val, typed(typ, orig))
}
// FixValue returns val after converting and truncating it as
// appropriate for typ.
func FixValue(typ *types.Type, val constant.Value) constant.Value {
assert(typ.Kind() != types.TFORW)
switch {
case typ.IsInteger():
val = constant.ToInt(val)
case typ.IsFloat():
val = constant.ToFloat(val)
case typ.IsComplex():
val = constant.ToComplex(val)
}
if !typ.IsUntyped() {
val = typecheck.DefaultLit(ir.NewBasicLit(src.NoXPos, val), typ).Val()
}
if typ.Kind() != types.TTYPEPARAM {
ir.AssertValidTypeForConst(typ, val)
}
return val
}
func Nil(pos src.XPos, typ *types.Type) ir.Node { func Nil(pos src.XPos, typ *types.Type) ir.Node {
return typed(typ, ir.NewNilExpr(pos)) return typed(typ, ir.NewNilExpr(pos))
} }