diff --git a/ssa/builder.go b/ssa/builder.go index 18982b5d..c9ab2c10 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -55,10 +55,10 @@ var ( tEface = new(types.Interface) // SSA Value constants. - vZero = intLiteral(0) - vOne = intLiteral(1) - vTrue = NewLiteral(exact.MakeBool(true), tBool) - vFalse = NewLiteral(exact.MakeBool(false), tBool) + vZero = intConst(0) + vOne = intConst(1) + vTrue = NewConst(exact.MakeBool(true), tBool) + vFalse = NewConst(exact.MakeBool(false), tBool) ) // builder holds state associated with the package currently being built. @@ -126,7 +126,7 @@ func (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) { } switch cond := b.expr(fn, e).(type) { - case *Literal: + case *Const: // Dispatch constant conditions statically. if exact.BoolVal(cond.Value) { emitJump(fn, t) @@ -313,7 +313,7 @@ func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types. t := deref(fn.Pkg.typeOf(args[0])).Underlying() if at, ok := t.(*types.Array); ok { b.expr(fn, args[0]) // for effects only - return intLiteral(at.Len()) + return intConst(at.Len()) } // Otherwise treat as normal. @@ -547,11 +547,11 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { // func (b *builder) expr(fn *Function, e ast.Expr) Value { if v := fn.Pkg.info.ValueOf(e); v != nil { - lit := NewLiteral(v, fn.Pkg.typeOf(e)) + c := NewConst(v, fn.Pkg.typeOf(e)) if id, ok := unparen(e).(*ast.Ident); ok { - emitDebugRef(fn, id, lit) + emitDebugRef(fn, id, c) } - return lit + return c } switch e := e.(type) { @@ -933,7 +933,7 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx st := sig.Params().At(np).Type().(*types.Slice) vt := st.Elem() if len(varargs) == 0 { - args = append(args, nilLiteral(st)) + args = append(args, nilConst(st)) } else { // Replace a suffix of args with a slice containing it. at := types.NewArray(vt, int64(len(varargs))) @@ -942,7 +942,7 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx for i, arg := range varargs { iaddr := &IndexAddr{ X: a, - Index: intLiteral(int64(i)), + Index: intConst(int64(i)), } iaddr.setType(types.NewPointer(vt)) fn.emit(iaddr) @@ -1120,8 +1120,6 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { for _, id := range spec.Names { if !isBlankIdent(id) { lhs := fn.addLocalForIdent(id) - // TODO(adonovan): opt: use zero literal in - // lieu of load, if type permits. if fn.debugInfo() { emitDebugRef(fn, id, emitLoad(fn, lhs)) } @@ -1201,7 +1199,7 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 { var i int64 = -1 for _, e := range elts { if kv, ok := e.(*ast.KeyValueExpr); ok { - i = b.expr(fn, kv.Key).(*Literal).Int64() + i = b.expr(fn, kv.Key).(*Const).Int64() } else { i++ } @@ -1258,17 +1256,17 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ array = addr } - var idx *Literal + var idx *Const for _, e := range e.Elts { if kv, ok := e.(*ast.KeyValueExpr); ok { - idx = b.expr(fn, kv.Key).(*Literal) + idx = b.expr(fn, kv.Key).(*Const) e = kv.Value } else { var idxval int64 if idx != nil { idxval = idx.Int64() + 1 } - idx = intLiteral(idxval) + idx = intConst(idxval) } iaddr := &IndexAddr{ X: array, @@ -1286,7 +1284,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ } case *types.Map: - m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts)))} + m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))} m.setPos(e.Lbrace) m.setType(typ) emitStore(fn, addr, fn.emit(m)) @@ -1474,7 +1472,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl casetype = fn.Pkg.typeOf(cond) var condv Value if casetype == tUntypedNil { - condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos) + condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos) } else { yok := emitTypeTest(fn, x, casetype, token.NoPos) ti = emitExtract(fn, yok, 0, casetype) @@ -1616,7 +1614,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { } body := fn.newBasicBlock("select.body") next := fn.newBasicBlock("select.next") - emitIf(fn, emitCompare(fn, token.EQL, idx, intLiteral(int64(state)), token.NoPos), body, next) + emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next) fn.currentBlock = body fn.targets = &targets{ tail: fn.targets, @@ -1741,7 +1739,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value // data dependence upon x, permitting later dead-code // elimination if x is pure, static unrolling, etc. // Ranging over a nil *array may have >0 iterations. - length = intLiteral(arr.Len()) + length = intConst(arr.Len()) } else { // length = len(x). var c Call @@ -1752,7 +1750,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value } index := fn.addLocal(tInt, token.NoPos) - emitStore(fn, index, intLiteral(-1)) + emitStore(fn, index, intConst(-1)) loop = fn.newBasicBlock("rangeindex.loop") emitJump(fn, loop) diff --git a/ssa/const.go b/ssa/const.go new file mode 100644 index 00000000..51bcf046 --- /dev/null +++ b/ssa/const.go @@ -0,0 +1,145 @@ +package ssa + +// This file defines the Const SSA value type. + +import ( + "fmt" + "go/token" + "strconv" + + "code.google.com/p/go.tools/go/exact" + "code.google.com/p/go.tools/go/types" +) + +// NewConst returns a new constant of the specified value and type. +// val must be valid according to the specification of Const.Value. +// +func NewConst(val exact.Value, typ types.Type) *Const { + return &Const{typ, val} +} + +// intConst returns an untyped integer constant that evaluates to i. +func intConst(i int64) *Const { + return NewConst(exact.MakeInt64(i), types.Typ[types.UntypedInt]) +} + +// nilConst returns a nil constant of the specified type, which may +// be any reference type, including interfaces. +// +func nilConst(typ types.Type) *Const { + return NewConst(exact.MakeNil(), typ) +} + +// zeroConst returns a new "zero" constant of the specified type, +// which must not be an array or struct type: the zero values of +// aggregates are well-defined but cannot be represented by Const. +// +func zeroConst(t types.Type) *Const { + switch t := t.(type) { + case *types.Basic: + switch { + case t.Info()&types.IsBoolean != 0: + return NewConst(exact.MakeBool(false), t) + case t.Info()&types.IsNumeric != 0: + return NewConst(exact.MakeInt64(0), t) + case t.Info()&types.IsString != 0: + return NewConst(exact.MakeString(""), t) + case t.Kind() == types.UnsafePointer: + fallthrough + case t.Kind() == types.UntypedNil: + return nilConst(t) + default: + panic(fmt.Sprint("zeroConst for unexpected type:", t)) + } + case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: + return nilConst(t) + case *types.Named: + return NewConst(zeroConst(t.Underlying()).Value, t) + case *types.Array, *types.Struct, *types.Tuple: + panic(fmt.Sprint("zeroConst applied to aggregate:", t)) + } + panic(fmt.Sprint("zeroConst: unexpected ", t)) +} + +func (c *Const) Name() string { + var s string + if c.Value.Kind() == exact.String { + s = exact.StringVal(c.Value) + const max = 20 + if len(s) > max { + s = s[:max-3] + "..." // abbreviate + } + s = strconv.Quote(s) + } else { + s = c.Value.String() + } + return s + ":" + c.typ.String() +} + +func (c *Const) Type() types.Type { + return c.typ +} + +func (c *Const) Referrers() *[]Instruction { + return nil +} + +func (c *Const) Pos() token.Pos { + return token.NoPos +} + +// IsNil returns true if this constant represents a typed or untyped nil value. +func (c *Const) IsNil() bool { + return c.Value.Kind() == exact.Nil +} + +// Int64 returns the numeric value of this constant truncated to fit +// a signed 64-bit integer. +// +func (c *Const) Int64() int64 { + switch x := c.Value; x.Kind() { + case exact.Int: + if i, ok := exact.Int64Val(x); ok { + return i + } + return 0 + case exact.Float: + f, _ := exact.Float64Val(x) + return int64(f) + } + panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) +} + +// Uint64 returns the numeric value of this constant truncated to fit +// an unsigned 64-bit integer. +// +func (c *Const) Uint64() uint64 { + switch x := c.Value; x.Kind() { + case exact.Int: + if u, ok := exact.Uint64Val(x); ok { + return u + } + return 0 + case exact.Float: + f, _ := exact.Float64Val(x) + return uint64(f) + } + panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) +} + +// Float64 returns the numeric value of this constant truncated to fit +// a float64. +// +func (c *Const) Float64() float64 { + f, _ := exact.Float64Val(c.Value) + return f +} + +// Complex128 returns the complex value of this constant truncated to +// fit a complex128. +// +func (c *Const) Complex128() complex128 { + re, _ := exact.Float64Val(exact.Real(c.Value)) + im, _ := exact.Float64Val(exact.Imag(c.Value)) + return complex(re, im) +} diff --git a/ssa/create.go b/ssa/create.go index 3e4d5bd0..5ca4bc01 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -89,9 +89,9 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { pkg.Members[name] = &Type{object: obj} case *types.Const: - c := &Constant{ + c := &NamedConst{ object: obj, - Value: NewLiteral(obj.Val(), obj.Type()), + Value: NewConst(obj.Val(), obj.Type()), } pkg.values[obj] = c.Value pkg.Members[name] = c diff --git a/ssa/doc.go b/ssa/doc.go index 99ac2a24..113dce8a 100644 --- a/ssa/doc.go +++ b/ssa/doc.go @@ -57,7 +57,7 @@ // *Capture ✔ // *ChangeInterface ✔ ✔ // *ChangeType ✔ ✔ -// *Constant ✔ (const) +// *Const ✔ // *Convert ✔ ✔ // *Defer ✔ // *Extract ✔ ✔ @@ -70,7 +70,6 @@ // *Index ✔ ✔ // *IndexAddr ✔ ✔ // *Jump ✔ -// *Literal ✔ // *Lookup ✔ ✔ // *MakeChan ✔ ✔ // *MakeClosure ✔ ✔ @@ -78,6 +77,7 @@ // *MakeMap ✔ ✔ // *MakeSlice ✔ ✔ // *MapUpdate ✔ +// *NamedConst ✔ (const) // *Next ✔ ✔ // *Panic ✔ // *Parameter ✔ diff --git a/ssa/emit.go b/ssa/emit.go index 54e74e87..62ec0e80 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -110,9 +110,9 @@ func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { y = emitConv(f, y, x.Type()) } else if _, ok := yt.(*types.Interface); ok { x = emitConv(f, x, y.Type()) - } else if _, ok := x.(*Literal); ok { + } else if _, ok := x.(*Const); ok { x = emitConv(f, x, y.Type()) - } else if _, ok := y.(*Literal); ok { + } else if _, ok := y.(*Const); ok { y = emitConv(f, y, x.Type()) } else { // other cases, e.g. channels. No-op. @@ -188,9 +188,9 @@ func emitConv(f *Function, val Value, typ types.Type) Value { return emitTypeAssert(f, val, typ, token.NoPos) } - // Untyped nil literal? Return interface-typed nil literal. + // Untyped nil constant? Return interface-typed nil constant. if ut_src == tUntypedNil { - return nilLiteral(typ) + return nilConst(typ) } // Convert (non-nil) "untyped" literals to their default type. @@ -203,13 +203,13 @@ func emitConv(f *Function, val Value, typ types.Type) Value { return f.emit(mi) } - // Conversion of a literal to a non-interface type results in - // a new literal of the destination type and (initially) the + // Conversion of a constant to a non-interface type results in + // a new constant of the destination type and (initially) the // same abstract value. We don't compute the representation // change yet; this defers the point at which the number of // possible representations explodes. - if l, ok := val.(*Literal); ok { - return NewLiteral(l.Value, typ) + if c, ok := val.(*Const); ok { + return NewConst(c.Value, typ) } // A representation-changing conversion. diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index ac83e673..82ae1ddc 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -110,8 +110,8 @@ func (fr *frame) get(key ssa.Value) value { return nil case *ssa.Function, *ssa.Builtin: return key - case *ssa.Literal: - return literalValue(key) + case *ssa.Const: + return constValue(key) case *ssa.Global: if r, ok := fr.i.globals[key]; ok { return r diff --git a/ssa/interp/ops.go b/ssa/interp/ops.go index e1bfb6db..fbab4286 100644 --- a/ssa/interp/ops.go +++ b/ssa/interp/ops.go @@ -20,60 +20,60 @@ type targetPanic struct { // If the target program calls exit, the interpreter panics with this type. type exitPanic int -// literalValue returns the value of the literal with the -// dynamic type tag appropriate for l.Type(). -func literalValue(l *ssa.Literal) value { - if l.IsNil() { - return zero(l.Type()) // typed nil +// constValue returns the value of the constant with the +// dynamic type tag appropriate for c.Type(). +func constValue(c *ssa.Const) value { + if c.IsNil() { + return zero(c.Type()) // typed nil } // By destination type: - switch t := l.Type().Underlying().(type) { + switch t := c.Type().Underlying().(type) { case *types.Basic: - // TODO(adonovan): eliminate untyped literals from SSA form. + // TODO(adonovan): eliminate untyped constants from SSA form. switch t.Kind() { case types.Bool, types.UntypedBool: - return exact.BoolVal(l.Value) + return exact.BoolVal(c.Value) case types.Int, types.UntypedInt: // Assume sizeof(int) is same on host and target. - return int(l.Int64()) + return int(c.Int64()) case types.Int8: - return int8(l.Int64()) + return int8(c.Int64()) case types.Int16: - return int16(l.Int64()) + return int16(c.Int64()) case types.Int32, types.UntypedRune: - return int32(l.Int64()) + return int32(c.Int64()) case types.Int64: - return l.Int64() + return c.Int64() case types.Uint: // Assume sizeof(uint) is same on host and target. - return uint(l.Uint64()) + return uint(c.Uint64()) case types.Uint8: - return uint8(l.Uint64()) + return uint8(c.Uint64()) case types.Uint16: - return uint16(l.Uint64()) + return uint16(c.Uint64()) case types.Uint32: - return uint32(l.Uint64()) + return uint32(c.Uint64()) case types.Uint64: - return l.Uint64() + return c.Uint64() case types.Uintptr: // Assume sizeof(uintptr) is same on host and target. - return uintptr(l.Uint64()) + return uintptr(c.Uint64()) case types.Float32: - return float32(l.Float64()) + return float32(c.Float64()) case types.Float64, types.UntypedFloat: - return l.Float64() + return c.Float64() case types.Complex64: - return complex64(l.Complex128()) + return complex64(c.Complex128()) case types.Complex128, types.UntypedComplex: - return l.Complex128() + return c.Complex128() case types.String, types.UntypedString: - if l.Value.Kind() == exact.String { - return exact.StringVal(l.Value) + if c.Value.Kind() == exact.String { + return exact.StringVal(c.Value) } - return string(rune(l.Int64())) + return string(rune(c.Int64())) case types.UnsafePointer: - panic("unsafe.Pointer literal") // not possible + panic("unsafe.Pointer constant") // not possible case types.UntypedNil: // nil was handled above. } @@ -84,13 +84,13 @@ func literalValue(l *ssa.Literal) value { switch et.Kind() { case types.Byte: // string -> []byte var v []value - for _, b := range []byte(exact.StringVal(l.Value)) { + for _, b := range []byte(exact.StringVal(c.Value)) { v = append(v, b) } return v case types.Rune: // string -> []rune var v []value - for _, r := range []rune(exact.StringVal(l.Value)) { + for _, r := range []rune(exact.StringVal(c.Value)) { v = append(v, r) } return v @@ -98,7 +98,7 @@ func literalValue(l *ssa.Literal) value { } } - panic(fmt.Sprintf("literalValue: Value.(type)=%T Type()=%s", l.Value, l.Type())) + panic(fmt.Sprintf("constValue: Value.(type)=%T Type()=%s", c.Value, c.Type())) } // asInt converts x, which must be an integer, to an int suitable for @@ -161,7 +161,7 @@ func zero(t types.Type) value { if t.Info()&types.IsUntyped != 0 { // TODO(adonovan): make it an invariant that // this is unreachable. Currently some - // literals have 'untyped' types when they + // constants have 'untyped' types when they // should be defaulted by the typechecker. t = ssa.DefaultType(t).(*types.Basic) } diff --git a/ssa/lift.go b/ssa/lift.go index bdffc133..5549e542 100644 --- a/ssa/lift.go +++ b/ssa/lift.go @@ -115,7 +115,7 @@ func lift(fn *Function) { // buildDomTree. For example: // // - Alloc never loaded? Eliminate. - // - Alloc never stored? Replace all loads with a zero literal. + // - Alloc never stored? Replace all loads with a zero constant. // - Alloc stored once? Replace loads with dominating store; // don't forget that an Alloc is itself an effective store // of zero. @@ -187,8 +187,8 @@ func lift(fn *Function) { // renaming maps an alloc (keyed by index) to its replacement // value. Initially the renaming contains nil, signifying the - // zero literal of the appropriate type; we construct the - // Literal lazily at most once on each path through the domtree. + // zero constant of the appropriate type; we construct the + // Const lazily at most once on each path through the domtree. // TODO(adonovan): opt: cache per-function not per subtree. renaming := make([]Value, numAllocs) @@ -304,8 +304,8 @@ type newPhiMap map[*BasicBlock][]newPhi // func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { // Don't lift aggregates into registers, because we don't have - // a way to express their zero-literals. - // TODO(adonovan): define zero-literals for aggregates, or + // a way to express their zero-constants. + // TODO(adonovan): define zero-constants for aggregates, or // add a separate SRA pass. Lifting aggregates is an // important optimisation for pointer analysis because the // extra indirection really hurts precision under Das's @@ -426,7 +426,7 @@ func replaceAll(x, y Value) { func renamed(renaming []Value, alloc *Alloc) Value { v := renaming[alloc.index] if v == nil { - v = zeroLiteral(deref(alloc.Type())) + v = zeroConst(deref(alloc.Type())) renaming[alloc.index] = v } return v diff --git a/ssa/literal.go b/ssa/literal.go deleted file mode 100644 index bbb68b2b..00000000 --- a/ssa/literal.go +++ /dev/null @@ -1,145 +0,0 @@ -package ssa - -// This file defines the Literal SSA value type. - -import ( - "fmt" - "go/token" - "strconv" - - "code.google.com/p/go.tools/go/exact" - "code.google.com/p/go.tools/go/types" -) - -// NewLiteral returns a new literal of the specified value and type. -// val must be valid according to the specification of Literal.Value. -// -func NewLiteral(val exact.Value, typ types.Type) *Literal { - return &Literal{typ, val} -} - -// intLiteral returns an untyped integer literal that evaluates to i. -func intLiteral(i int64) *Literal { - return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt]) -} - -// nilLiteral returns a nil literal of the specified type, which may -// be any reference type, including interfaces. -// -func nilLiteral(typ types.Type) *Literal { - return NewLiteral(exact.MakeNil(), typ) -} - -// zeroLiteral returns a new "zero" literal of the specified type, -// which must not be an array or struct type: the zero values of -// aggregates are well-defined but cannot be represented by Literal. -// -func zeroLiteral(t types.Type) *Literal { - switch t := t.(type) { - case *types.Basic: - switch { - case t.Info()&types.IsBoolean != 0: - return NewLiteral(exact.MakeBool(false), t) - case t.Info()&types.IsNumeric != 0: - return NewLiteral(exact.MakeInt64(0), t) - case t.Info()&types.IsString != 0: - return NewLiteral(exact.MakeString(""), t) - case t.Kind() == types.UnsafePointer: - fallthrough - case t.Kind() == types.UntypedNil: - return nilLiteral(t) - default: - panic(fmt.Sprint("zeroLiteral for unexpected type:", t)) - } - case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: - return nilLiteral(t) - case *types.Named: - return NewLiteral(zeroLiteral(t.Underlying()).Value, t) - case *types.Array, *types.Struct: - panic(fmt.Sprint("zeroLiteral applied to aggregate:", t)) - } - panic(fmt.Sprint("zeroLiteral: unexpected ", t)) -} - -func (l *Literal) Name() string { - var s string - if l.Value.Kind() == exact.String { - s = exact.StringVal(l.Value) - const maxLit = 20 - if len(s) > maxLit { - s = s[:maxLit-3] + "..." // abbreviate - } - s = strconv.Quote(s) - } else { - s = l.Value.String() - } - return s + ":" + l.typ.String() -} - -func (l *Literal) Type() types.Type { - return l.typ -} - -func (l *Literal) Referrers() *[]Instruction { - return nil -} - -func (l *Literal) Pos() token.Pos { - return token.NoPos -} - -// IsNil returns true if this literal represents a typed or untyped nil value. -func (l *Literal) IsNil() bool { - return l.Value.Kind() == exact.Nil -} - -// Int64 returns the numeric value of this literal truncated to fit -// a signed 64-bit integer. -// -func (l *Literal) Int64() int64 { - switch x := l.Value; x.Kind() { - case exact.Int: - if i, ok := exact.Int64Val(x); ok { - return i - } - return 0 - case exact.Float: - f, _ := exact.Float64Val(x) - return int64(f) - } - panic(fmt.Sprintf("unexpected literal value: %T", l.Value)) -} - -// Uint64 returns the numeric value of this literal truncated to fit -// an unsigned 64-bit integer. -// -func (l *Literal) Uint64() uint64 { - switch x := l.Value; x.Kind() { - case exact.Int: - if u, ok := exact.Uint64Val(x); ok { - return u - } - return 0 - case exact.Float: - f, _ := exact.Float64Val(x) - return uint64(f) - } - panic(fmt.Sprintf("unexpected literal value: %T", l.Value)) -} - -// Float64 returns the numeric value of this literal truncated to fit -// a float64. -// -func (l *Literal) Float64() float64 { - f, _ := exact.Float64Val(l.Value) - return f -} - -// Complex128 returns the complex value of this literal truncated to -// fit a complex128. -// -func (l *Literal) Complex128() complex128 { - re, _ := exact.Float64Val(exact.Real(l.Value)) - im, _ := exact.Float64Val(exact.Imag(l.Value)) - return complex(re, im) -} diff --git a/ssa/print.go b/ssa/print.go index 2fba1a8a..d0a46a34 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -47,7 +47,7 @@ func relName(v Value, i Instruction) string { // This method is provided only for debugging. // It never appears in disassembly, which uses Value.Name(). -func (v *Literal) String() string { +func (v *Const) String() string { return v.Name() } @@ -379,7 +379,7 @@ func (p *Package) DumpTo(w io.Writer) { sort.Strings(names) for _, name := range names { switch mem := p.Members[name].(type) { - case *Constant: + case *NamedConst: fmt.Fprintf(w, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.Name()) case *Function: diff --git a/ssa/sanity.go b/ssa/sanity.go index 0d326e2c..8869befa 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -173,8 +173,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { } } - // TODO(adonovan): sanity-check Literals used as instruction Operands(), - // e.g. reject Literals with "untyped" types. + // TODO(adonovan): sanity-check Consts used as instruction Operands(), + // e.g. reject Consts with "untyped" types. // // All other non-Instruction Values can be found via their // enclosing Function or Package. diff --git a/ssa/source.go b/ssa/source.go index f3f2ca91..914a08d8 100644 --- a/ssa/source.go +++ b/ssa/source.go @@ -201,7 +201,7 @@ func (prog *Program) Package(obj *types.Package) *Package { // packageLevelValue returns the package-level value corresponding to // the specified named object, which may be a package-level const -// (*Literal), var (*Global) or func (*Function) of some package in +// (*Const), var (*Global) or func (*Function) of some package in // prog. It returns nil if the object is not found. // func (prog *Program) packageLevelValue(obj types.Object) Value { @@ -233,21 +233,21 @@ func (prog *Program) FuncValue(obj *types.Func) Value { } // ConstValue returns the SSA Value denoted by the source-level named -// constant obj. The result may be a *Literal, or nil if not found. +// constant obj. The result may be a *Const, or nil if not found. // -func (prog *Program) ConstValue(obj *types.Const) *Literal { +func (prog *Program) ConstValue(obj *types.Const) *Const { // TODO(adonovan): opt: share (don't reallocate) - // Literals for const objects. + // Consts for const objects. // Universal constant? {true,false,nil} if obj.Parent() == types.Universe { - return NewLiteral(obj.Val(), obj.Type()) + return NewConst(obj.Val(), obj.Type()) } // Package-level named constant? if v := prog.packageLevelValue(obj); v != nil { - return v.(*Literal) + return v.(*Const) } - return NewLiteral(obj.Val(), obj.Type()) + return NewConst(obj.Val(), obj.Type()) } // VarValue returns the SSA Value that corresponds to a specific diff --git a/ssa/source_test.go b/ssa/source_test.go index 8ed82b20..b76fe08c 100644 --- a/ssa/source_test.go +++ b/ssa/source_test.go @@ -25,7 +25,7 @@ func TestObjValueLookup(t *testing.T) { } // Maps each var Ident (represented "name:linenum") to the - // kind of ssa.Value we expect (represented "Literal", "&Alloc"). + // kind of ssa.Value we expect (represented "Constant", "&Alloc"). expectations := make(map[string]string) // Find all annotations of form x::BinOp, &y::Alloc, etc. @@ -127,20 +127,20 @@ func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { } func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) { - lit := prog.ConstValue(obj) - // fmt.Printf("ConstValue(%s) = %s\n", obj, lit) // debugging - if lit == nil { + c := prog.ConstValue(obj) + // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging + if c == nil { t.Errorf("ConstValue(%s) == nil", obj) return } - if !types.IsIdentical(lit.Type(), obj.Type()) { - t.Errorf("ConstValue(%s).Type() == %s", obj, lit.Type()) + if !types.IsIdentical(c.Type(), obj.Type()) { + t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type()) return } if obj.Name() != "nil" { - if !exact.Compare(lit.Value, token.EQL, obj.Val()) { + if !exact.Compare(c.Value, token.EQL, obj.Val()) { t.Errorf("ConstValue(%s).Value (%s) != %s", - obj, lit.Value, obj.Val()) + obj, c.Value, obj.Val()) return } } diff --git a/ssa/ssa.go b/ssa/ssa.go index 54728351..bd4b4723 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -50,7 +50,7 @@ type Package struct { info *importer.PackageInfo // package ASTs and type information } -// A Member is a member of a Go package, implemented by *Constant, +// A Member is a member of a Go package, implemented by *NamedConst, // *Global, *Function, or *Type; they are created by package-level // const, var, func and type declarations respectively. // @@ -100,18 +100,18 @@ type Type struct { object *types.TypeName } -// A Constant is a Member of Package representing a package-level -// constant value. +// A NamedConst is a Member of Package representing a package-level +// named constant value. // // Pos() returns the position of the declaring ast.ValueSpec.Names[*] // identifier. // -// NB: a Constant is not a Value; it contains a literal Value, which +// NB: a NamedConst is not a Value; it contains a constant Value, which // it augments with the name and position of its 'const' declaration. // -type Constant struct { +type NamedConst struct { object *types.Const - Value *Literal + Value *Const pos token.Pos } @@ -123,7 +123,7 @@ type Value interface { // // This is the same as the source name for Parameters, // Builtins, Functions, Captures, Globals and some Allocs. - // For literals, it is a representation of the literal's value + // For constants, it is a representation of the constant's value // and type. For all other Values this is the name of the // virtual register defined by the instruction. // @@ -151,7 +151,7 @@ type Value interface { // // Referrers is currently only defined for the function-local // values Capture, Parameter and all value-defining instructions. - // It returns nil for Function, Builtin, Literal and Global. + // It returns nil for Function, Builtin, Const and Global. // // Instruction.Operands contains the inverse of this relation. Referrers() *[]Instruction @@ -358,22 +358,22 @@ type Parameter struct { referrers []Instruction } -// A Literal represents the value of a constant expression. +// A Const represents the value of a constant expression. // // It may have a nil, boolean, string or numeric (integer, fraction or // complex) value, or a []byte or []rune conversion of a string -// literal. +// constant. // -// Literals may be of named types. A literal's underlying type can be +// Consts may be of named types. A constant's underlying type can be // a basic type, possibly one of the "untyped" types, or a slice type -// whose elements' underlying type is byte or rune. A nil literal can +// whose elements' underlying type is byte or rune. A nil constant can // have any reference type: interface, map, channel, pointer, slice, // or function---but not "untyped nil". // -// All source-level constant expressions are represented by a Literal +// All source-level constant expressions are represented by a Const // of equal type and value. // -// Value holds the exact value of the literal, independent of its +// Value holds the exact value of the constant, independent of its // Type(), using the same representation as package go/exact uses for // constants. // @@ -384,7 +384,7 @@ type Parameter struct { // "hello":untyped string // 3+4i:MyComplex // -type Literal struct { +type Const struct { typ types.Type Value exact.Value } @@ -619,7 +619,7 @@ type ChangeInterface struct { // Use Program.MethodSet(X.Type()) to find the method-set of X. // // To construct the zero value of an interface type T, use: -// NewLiteral(exact.MakeNil(), T, pos) +// NewConst(exact.MakeNil(), T, pos) // // Pos() returns the ast.CallExpr.Lparen, if the instruction arose // from an explicit conversion in the source. @@ -1384,14 +1384,14 @@ func (t *Type) String() string { return fmt.Sprintf("%s.%s", t.object.Pkg().Path(), t.object.Name()) } -func (c *Constant) Name() string { return c.object.Name() } -func (c *Constant) Pos() token.Pos { return c.object.Pos() } -func (c *Constant) String() string { +func (c *NamedConst) Name() string { return c.object.Name() } +func (c *NamedConst) Pos() token.Pos { return c.object.Pos() } +func (c *NamedConst) String() string { return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name()) } -func (c *Constant) Type() types.Type { return c.object.Type() } -func (c *Constant) Token() token.Token { return token.CONST } -func (c *Constant) Object() types.Object { return c.object } +func (c *NamedConst) Type() types.Type { return c.object.Type() } +func (c *NamedConst) Token() token.Token { return token.CONST } +func (c *NamedConst) Object() types.Object { return c.object } // Func returns the package-level function of the specified name, // or nil if not found. @@ -1412,8 +1412,8 @@ func (p *Package) Var(name string) (g *Global) { // Const returns the package-level constant of the specified name, // or nil if not found. // -func (p *Package) Const(name string) (c *Constant) { - c, _ = p.Members[name].(*Constant) +func (p *Package) Const(name string) (c *NamedConst) { + c, _ = p.Members[name].(*NamedConst) return } diff --git a/ssa/testdata/objlookup.go b/ssa/testdata/objlookup.go index 86eec80c..560c30df 100644 --- a/ssa/testdata/objlookup.go +++ b/ssa/testdata/objlookup.go @@ -35,28 +35,28 @@ type S struct { } func main() { - var v0 int = 1 // v0::Literal (simple local value spec) - if v0 > 0 { // v0::Literal - v0 = 2 // v0::Literal + var v0 int = 1 // v0::Const (simple local value spec) + if v0 > 0 { // v0::Const + v0 = 2 // v0::Const } print(v0) // v0::Phi // v1 is captured and thus implicitly address-taken. - var v1 int = 1 // v1::Literal - v1 = 2 // v1::Literal + var v1 int = 1 // v1::Const + v1 = 2 // v1::Const fmt.Println(v1) // v1::UnOp (load) f := func(param int) { // f::MakeClosure param::Parameter - if y := 1; y > 0 { // y::Literal + if y := 1; y > 0 { // y::Const print(v1, param) // v1::UnOp (load) param::Parameter } - param = 2 // param::Literal - println(param) // param::Literal + param = 2 // param::Const + println(param) // param::Const } f(0) // f::MakeClosure - var v2 int // v2::Literal (implicitly zero-initialized local value spec) - print(v2) // v2::Literal + var v2 int // v2::Const (implicitly zero-initialized local value spec) + print(v2) // v2::Const m := make(map[string]int) // m::MakeMap @@ -68,9 +68,9 @@ func main() { v3++ // v3::BinOp (assign with op) v3 += 2 // v3::BinOp (assign with op) - v5, v6 := false, "" // v5::Literal v6::Literal (defining assignment) - print(v5) // v5::Literal - print(v6) // v6::Literal + v5, v6 := false, "" // v5::Const v6::Const (defining assignment) + print(v5) // v5::Const + print(v6) // v6::Const var v7 S // v7::UnOp (load from Alloc) v7.x = 1 // &v7::Alloc @@ -88,8 +88,8 @@ func main() { v10 := &v9 // v10::Alloc &v9::Alloc - var v11 *J = nil // v11::Literal - v11.method() // v11::Literal + var v11 *J = nil // v11::Const + v11.method() // v11::Const var v12 J // v12::UnOp (load from Alloc) v12.method() // &v12::Alloc (implicitly address-taken) @@ -100,7 +100,7 @@ func main() { println(v13) // v13::nil } - switch x := 1; x { // x::Literal + switch x := 1; x { // x::Const case v0: // v0::Phi } @@ -108,10 +108,10 @@ func main() { v++ // v::BinOp } - if y := 0; y > 1 { // y::Literal y::Literal + if y := 0; y > 1 { // y::Const y::Const } - var i interface{} // i::Literal (nil interface) + var i interface{} // i::Const (nil interface) i = 1 // i::MakeInterface switch i := i.(type) { // i::MakeInterface i::MakeInterface case int: