go.tools/ssa: remove position info from Literals.

R=gri
CC=golang-dev
https://golang.org/cl/11292043
This commit is contained in:
Alan Donovan 2013-07-15 16:10:08 -04:00
parent 55d678e697
commit a399e26e0e
9 changed files with 36 additions and 50 deletions

View File

@ -57,8 +57,8 @@ var (
// SSA Value constants. // SSA Value constants.
vZero = intLiteral(0) vZero = intLiteral(0)
vOne = intLiteral(1) vOne = intLiteral(1)
vTrue = NewLiteral(exact.MakeBool(true), tBool, token.NoPos) vTrue = NewLiteral(exact.MakeBool(true), tBool)
vFalse = NewLiteral(exact.MakeBool(false), tBool, token.NoPos) vFalse = NewLiteral(exact.MakeBool(false), tBool)
) )
// builder holds state associated with the package currently being built. // builder holds state associated with the package currently being built.
@ -547,12 +547,7 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
// //
func (b *builder) expr(fn *Function, e ast.Expr) Value { func (b *builder) expr(fn *Function, e ast.Expr) Value {
if v := fn.Pkg.info.ValueOf(e); v != nil { if v := fn.Pkg.info.ValueOf(e); v != nil {
// TODO(adonovan): if e is an ident referring to a named lit := NewLiteral(v, fn.Pkg.typeOf(e))
// Const, should we share the Constant's literal?
// Then it won't have a position within the function.
// Do we want it to have the position of the Ident or
// the definition of the const expression?
lit := NewLiteral(v, fn.Pkg.typeOf(e), CanonicalPos(e))
if id, ok := unparen(e).(*ast.Ident); ok { if id, ok := unparen(e).(*ast.Ident); ok {
emitDebugRef(fn, id, lit) emitDebugRef(fn, id, lit)
} }

View File

@ -91,7 +91,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
case *types.Const: case *types.Const:
c := &Constant{ c := &Constant{
object: obj, object: obj,
Value: NewLiteral(obj.Val(), obj.Type(), obj.Pos()), Value: NewLiteral(obj.Val(), obj.Type()),
} }
pkg.values[obj] = c.Value pkg.values[obj] = c.Value
pkg.Members[name] = c pkg.Members[name] = c

View File

@ -210,7 +210,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
// change yet; this defers the point at which the number of // change yet; this defers the point at which the number of
// possible representations explodes. // possible representations explodes.
if l, ok := val.(*Literal); ok { if l, ok := val.(*Literal); ok {
return NewLiteral(l.Value, typ, l.Pos()) return NewLiteral(l.Value, typ)
} }
// A representation-changing conversion. // A representation-changing conversion.

View File

@ -159,6 +159,10 @@ func zero(t types.Type) value {
panic("untyped nil has no zero value") panic("untyped nil has no zero value")
} }
if t.Info()&types.IsUntyped != 0 { if t.Info()&types.IsUntyped != 0 {
// TODO(adonovan): make it an invariant that
// this is unreachable. Currently some
// literals have 'untyped' types when they
// should be defaulted by the typechecker.
t = ssa.DefaultType(t).(*types.Basic) t = ssa.DefaultType(t).(*types.Basic)
} }
switch t.Kind() { switch t.Kind() {

View File

@ -11,23 +11,23 @@ import (
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
) )
// NewLiteral returns a new literal of the specified value, type and position. // NewLiteral returns a new literal of the specified value and type.
// val must be valid according to the specification of Literal.Value. // val must be valid according to the specification of Literal.Value.
// //
func NewLiteral(val exact.Value, typ types.Type, pos token.Pos) *Literal { func NewLiteral(val exact.Value, typ types.Type) *Literal {
return &Literal{typ, val, pos} return &Literal{typ, val}
} }
// intLiteral returns an untyped integer literal that evaluates to i. // intLiteral returns an untyped integer literal that evaluates to i.
func intLiteral(i int64) *Literal { func intLiteral(i int64) *Literal {
return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt], token.NoPos) return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
} }
// nilLiteral returns a nil literal of the specified type, which may // nilLiteral returns a nil literal of the specified type, which may
// be any reference type, including interfaces. // be any reference type, including interfaces.
// //
func nilLiteral(typ types.Type) *Literal { func nilLiteral(typ types.Type) *Literal {
return NewLiteral(exact.MakeNil(), typ, token.NoPos) return NewLiteral(exact.MakeNil(), typ)
} }
// zeroLiteral returns a new "zero" literal of the specified type, // zeroLiteral returns a new "zero" literal of the specified type,
@ -39,11 +39,11 @@ func zeroLiteral(t types.Type) *Literal {
case *types.Basic: case *types.Basic:
switch { switch {
case t.Info()&types.IsBoolean != 0: case t.Info()&types.IsBoolean != 0:
return NewLiteral(exact.MakeBool(false), t, token.NoPos) return NewLiteral(exact.MakeBool(false), t)
case t.Info()&types.IsNumeric != 0: case t.Info()&types.IsNumeric != 0:
return NewLiteral(exact.MakeInt64(0), t, token.NoPos) return NewLiteral(exact.MakeInt64(0), t)
case t.Info()&types.IsString != 0: case t.Info()&types.IsString != 0:
return NewLiteral(exact.MakeString(""), t, token.NoPos) return NewLiteral(exact.MakeString(""), t)
case t.Kind() == types.UnsafePointer: case t.Kind() == types.UnsafePointer:
fallthrough fallthrough
case t.Kind() == types.UntypedNil: case t.Kind() == types.UntypedNil:
@ -54,7 +54,7 @@ func zeroLiteral(t types.Type) *Literal {
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
return nilLiteral(t) return nilLiteral(t)
case *types.Named: case *types.Named:
return NewLiteral(zeroLiteral(t.Underlying()).Value, t, token.NoPos) return NewLiteral(zeroLiteral(t.Underlying()).Value, t)
case *types.Array, *types.Struct: case *types.Array, *types.Struct:
panic(fmt.Sprint("zeroLiteral applied to aggregate:", t)) panic(fmt.Sprint("zeroLiteral applied to aggregate:", t))
} }
@ -85,7 +85,7 @@ func (l *Literal) Referrers() *[]Instruction {
} }
func (l *Literal) Pos() token.Pos { func (l *Literal) Pos() token.Pos {
return l.pos return token.NoPos
} }
// IsNil returns true if this literal represents a typed or untyped nil value. // IsNil returns true if this literal represents a typed or untyped nil value.

View File

@ -37,9 +37,9 @@ func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end to
continue continue
} }
if path, exact := PathEnclosingInterval(f, start, end); path != nil { if path, exact := PathEnclosingInterval(f, start, end); path != nil {
// TODO(adonovan): return the // TODO(adonovan): return info in lieu
// importPath; remove Prog as a // of pkg; remove Prog as a parameter;
// parameter. // move to importer.
return prog.PackagesByPath[importPath], path, exact return prog.PackagesByPath[importPath], path, exact
} }
} }
@ -171,24 +171,21 @@ func CanonicalPos(n ast.Node) token.Pos {
return CanonicalPos(n.X) return CanonicalPos(n.X)
case *ast.CallExpr: case *ast.CallExpr:
// f(x): *Call, *Go, *Defer, *Literal (e.g. len) // f(x): *Call, *Go, *Defer.
// T(x): *ChangeType, *Convert, *MakeInterface, *ChangeInterface, *Literal. // T(x): *ChangeType, *Convert, *MakeInterface, *ChangeInterface.
// make(): *MakeMap, *MakeChan, *MakeSlice. // make(): *MakeMap, *MakeChan, *MakeSlice.
// new(): *Alloc. // new(): *Alloc.
// panic(): *Panic. // panic(): *Panic.
return n.Lparen return n.Lparen
case *ast.BasicLit:
return n.ValuePos // *Literal
case *ast.Ident: case *ast.Ident:
return n.NamePos // *Parameter, *Alloc, *Capture, *Literal return n.NamePos // *Parameter, *Alloc, *Capture
case *ast.TypeAssertExpr: case *ast.TypeAssertExpr:
return n.Lparen // *ChangeInterface or *TypeAssertExpr return n.Lparen // *ChangeInterface or *TypeAssertExpr
case *ast.SelectorExpr: case *ast.SelectorExpr:
return n.Sel.NamePos // *MakeClosure, *Field, *FieldAddr, *Literal return n.Sel.NamePos // *MakeClosure, *Field, *FieldAddr
case *ast.FuncLit: case *ast.FuncLit:
return n.Type.Func // *Function or *MakeClosure return n.Type.Func // *Function or *MakeClosure
@ -197,10 +194,10 @@ func CanonicalPos(n ast.Node) token.Pos {
return n.Lbrace // *Alloc or *Slice return n.Lbrace // *Alloc or *Slice
case *ast.BinaryExpr: case *ast.BinaryExpr:
return n.OpPos // *Phi, *BinOp or *Literal return n.OpPos // *Phi or *BinOp
case *ast.UnaryExpr: case *ast.UnaryExpr:
return n.OpPos // *Phi, *UnOp, or *Literal return n.OpPos // *Phi or *UnOp
case *ast.IndexExpr: case *ast.IndexExpr:
return n.Lbrack // *Index or *IndexAddr return n.Lbrack // *Index or *IndexAddr
@ -277,25 +274,18 @@ func (prog *Program) FuncValue(obj *types.Func) Value {
// constant obj. The result may be a *Literal, or nil if not found. // constant obj. The result may be a *Literal, or nil if not found.
// //
func (prog *Program) ConstValue(obj *types.Const) *Literal { func (prog *Program) ConstValue(obj *types.Const) *Literal {
// TODO(adonovan): opt: share (don't reallocate)
// Literals for const objects.
// Universal constant? {true,false,nil} // Universal constant? {true,false,nil}
if obj.Parent() == types.Universe { if obj.Parent() == types.Universe {
// TODO(adonovan): opt: share, don't reallocate. return NewLiteral(obj.Val(), obj.Type())
return NewLiteral(obj.Val(), obj.Type(), obj.Pos())
} }
// Package-level named constant? // Package-level named constant?
if v := prog.packageLevelValue(obj); v != nil { if v := prog.packageLevelValue(obj); v != nil {
return v.(*Literal) return v.(*Literal)
} }
// TODO(adonovan): need a per-function const object map. For return NewLiteral(obj.Val(), obj.Type())
// now, just return a new literal.
//
// Design question: should literal (constant) values even have
// a position? Is their identity important? Should two
// different references to Math.pi be distinguishable in any
// way? From an analytical perspective, their type and value
// tell you all you need to know; they're interchangeable.
// Experiment with removing Literal.Pos().
return NewLiteral(obj.Val(), obj.Type(), obj.Pos())
} }
// VarValue returns the SSA Value that corresponds to a specific // VarValue returns the SSA Value that corresponds to a specific

View File

@ -4,7 +4,7 @@ package ssa
// It has no dependencies on ssa or go/types. // It has no dependencies on ssa or go/types.
// TODO(adonovan): move it somewhere more general, // TODO(adonovan): move it somewhere more general,
// e.g. go.tools/astutil? // e.g. go.tools/importer?
import ( import (
"fmt" "fmt"

View File

@ -299,7 +299,6 @@ func TestObjValueLookup(t *testing.T) {
for _, c := range f.Comments { for _, c := range f.Comments {
text := c.Text() text := c.Text()
pos := imp.Fset.Position(c.Pos()) pos := imp.Fset.Position(c.Pos())
fmt.Println(pos.Line, text)
for _, m := range re.FindAllStringSubmatch(text, -1) { for _, m := range re.FindAllStringSubmatch(text, -1) {
key := fmt.Sprintf("%s:%d", m[2], pos.Line) key := fmt.Sprintf("%s:%d", m[2], pos.Line)
value := m[1] + m[3] value := m[1] + m[3]

View File

@ -377,8 +377,7 @@ type Parameter struct {
// Type(), using the same representation as package go/exact uses for // Type(), using the same representation as package go/exact uses for
// constants. // constants.
// //
// Pos() returns the canonical position (see CanonicalPos) of the // Pos() returns token.NoPos.
// originating constant expression, if explicit in the source.
// //
// Example printed form: // Example printed form:
// 42:int // 42:int
@ -388,7 +387,6 @@ type Parameter struct {
type Literal struct { type Literal struct {
typ types.Type typ types.Type
Value exact.Value Value exact.Value
pos token.Pos
} }
// A Global is a named Value holding the address of a package-level // A Global is a named Value holding the address of a package-level
@ -1165,7 +1163,7 @@ type DebugRef struct {
// Register is a mix-in embedded by all SSA values that are also // Register is a mix-in embedded by all SSA values that are also
// instructions, i.e. virtual registers, and provides implementations // instructions, i.e. virtual registers, and provides implementations
// of the Value interface's Name() and Type() methods: the name is // of the Value interface's Name() and Type() methods: the name is
// simply a numbered register (e.g. "t0") and the type is the Type_ // simply a numbered register (e.g. "t0") and the type is the typ
// field. // field.
// //
// Temporary names are automatically assigned to each Register on // Temporary names are automatically assigned to each Register on