| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssa |
| |
| // This file defines the Const SSA value type. |
| |
| import ( |
| "fmt" |
| "go/constant" |
| "go/token" |
| "go/types" |
| "strconv" |
| "strings" |
| |
| "golang.org/x/tools/internal/aliases" |
| "golang.org/x/tools/internal/typeparams" |
| ) |
| |
| // 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 constant.Value, typ types.Type) *Const { |
| if val == nil { |
| switch soleTypeKind(typ) { |
| case types.IsBoolean: |
| val = constant.MakeBool(false) |
| case types.IsInteger: |
| val = constant.MakeInt64(0) |
| case types.IsString: |
| val = constant.MakeString("") |
| } |
| } |
| return &Const{typ, val} |
| } |
| |
| // soleTypeKind returns a BasicInfo for which constant.Value can |
| // represent all zero values for the types in the type set. |
| // |
| // types.IsBoolean for false is a representative. |
| // types.IsInteger for 0 |
| // types.IsString for "" |
| // 0 otherwise. |
| func soleTypeKind(typ types.Type) types.BasicInfo { |
| // State records the set of possible zero values (false, 0, ""). |
| // Candidates (perhaps all) are eliminated during the type-set |
| // iteration, which executes at least once. |
| state := types.IsBoolean | types.IsInteger | types.IsString |
| underIs(typeSetOf(typ), func(ut types.Type) bool { |
| var c types.BasicInfo |
| if t, ok := ut.(*types.Basic); ok { |
| c = t.Info() |
| } |
| if c&types.IsNumeric != 0 { // int/float/complex |
| c = types.IsInteger |
| } |
| state = state & c |
| return state != 0 |
| }) |
| return state |
| } |
| |
| // intConst returns an 'int' constant that evaluates to i. |
| // (i is an int64 in case the host is narrower than the target.) |
| func intConst(i int64) *Const { |
| return NewConst(constant.MakeInt64(i), tInt) |
| } |
| |
| // stringConst returns a 'string' constant that evaluates to s. |
| func stringConst(s string) *Const { |
| return NewConst(constant.MakeString(s), tString) |
| } |
| |
| // zeroConst returns a new "zero" constant of the specified type. |
| func zeroConst(t types.Type) *Const { |
| return NewConst(nil, t) |
| } |
| |
| func (c *Const) RelString(from *types.Package) string { |
| var s string |
| if c.Value == nil { |
| s = zeroString(c.typ, from) |
| } else if c.Value.Kind() == constant.String { |
| s = constant.StringVal(c.Value) |
| const max = 20 |
| // TODO(adonovan): don't cut a rune in half. |
| if len(s) > max { |
| s = s[:max-3] + "..." // abbreviate |
| } |
| s = strconv.Quote(s) |
| } else { |
| s = c.Value.String() |
| } |
| return s + ":" + relType(c.Type(), from) |
| } |
| |
| // zeroString returns the string representation of the "zero" value of the type t. |
| func zeroString(t types.Type, from *types.Package) string { |
| switch t := t.(type) { |
| case *types.Basic: |
| switch { |
| case t.Info()&types.IsBoolean != 0: |
| return "false" |
| case t.Info()&types.IsNumeric != 0: |
| return "0" |
| case t.Info()&types.IsString != 0: |
| return `""` |
| case t.Kind() == types.UnsafePointer: |
| fallthrough |
| case t.Kind() == types.UntypedNil: |
| return "nil" |
| default: |
| panic(fmt.Sprint("zeroString for unexpected type:", t)) |
| } |
| case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: |
| return "nil" |
| case *types.Named, *aliases.Alias: |
| return zeroString(t.Underlying(), from) |
| case *types.Array, *types.Struct: |
| return relType(t, from) + "{}" |
| case *types.Tuple: |
| // Tuples are not normal values. |
| // We are currently format as "(t[0], ..., t[n])". Could be something else. |
| components := make([]string, t.Len()) |
| for i := 0; i < t.Len(); i++ { |
| components[i] = zeroString(t.At(i).Type(), from) |
| } |
| return "(" + strings.Join(components, ", ") + ")" |
| case *types.TypeParam: |
| return "*new(" + relType(t, from) + ")" |
| } |
| panic(fmt.Sprint("zeroString: unexpected ", t)) |
| } |
| |
| func (c *Const) Name() string { |
| return c.RelString(nil) |
| } |
| |
| func (c *Const) String() string { |
| return c.Name() |
| } |
| |
| func (c *Const) Type() types.Type { |
| return c.typ |
| } |
| |
| func (c *Const) Referrers() *[]Instruction { |
| return nil |
| } |
| |
| func (c *Const) Parent() *Function { return nil } |
| |
| func (c *Const) Pos() token.Pos { |
| return token.NoPos |
| } |
| |
| // IsNil returns true if this constant is a nil value of |
| // a nillable reference type (pointer, slice, channel, map, or function), |
| // a basic interface type, or |
| // a type parameter all of whose possible instantiations are themselves nillable. |
| func (c *Const) IsNil() bool { |
| return c.Value == nil && nillable(c.typ) |
| } |
| |
| // nillable reports whether *new(T) == nil is legal for type T. |
| func nillable(t types.Type) bool { |
| if typeparams.IsTypeParam(t) { |
| return underIs(typeSetOf(t), func(u types.Type) bool { |
| // empty type set (u==nil) => any underlying types => not nillable |
| return u != nil && nillable(u) |
| }) |
| } |
| switch t.Underlying().(type) { |
| case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: |
| return true |
| case *types.Interface: |
| return true // basic interface. |
| default: |
| return false |
| } |
| } |
| |
| // TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp. |
| |
| // Int64 returns the numeric value of this constant truncated to fit |
| // a signed 64-bit integer. |
| func (c *Const) Int64() int64 { |
| switch x := constant.ToInt(c.Value); x.Kind() { |
| case constant.Int: |
| if i, ok := constant.Int64Val(x); ok { |
| return i |
| } |
| return 0 |
| case constant.Float: |
| f, _ := constant.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 := constant.ToInt(c.Value); x.Kind() { |
| case constant.Int: |
| if u, ok := constant.Uint64Val(x); ok { |
| return u |
| } |
| return 0 |
| case constant.Float: |
| f, _ := constant.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 { |
| x := constant.ToFloat(c.Value) // (c.Value == nil) => x.Kind() == Unknown |
| f, _ := constant.Float64Val(x) |
| return f |
| } |
| |
| // Complex128 returns the complex value of this constant truncated to |
| // fit a complex128. |
| func (c *Const) Complex128() complex128 { |
| x := constant.ToComplex(c.Value) // (c.Value == nil) => x.Kind() == Unknown |
| re, _ := constant.Float64Val(constant.Real(x)) |
| im, _ := constant.Float64Val(constant.Imag(x)) |
| return complex(re, im) |
| } |