194 lines
5.1 KiB
Go
194 lines
5.1 KiB
Go
// 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"
|
|
|
|
"golang.org/x/tools/internal/typeparams"
|
|
"golang.org/x/tools/internal/typesinternal"
|
|
)
|
|
|
|
// 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(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, _ = typesinternal.ZeroString(c.typ, types.RelativeTo(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)
|
|
}
|
|
|
|
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(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)
|
|
}
|