const.go raw

   1  // Copyright 2013 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package ssa
   6  
   7  // This file defines the Const SSA value type.
   8  
   9  import (
  10  	"fmt"
  11  	"go/constant"
  12  	"go/token"
  13  	"go/types"
  14  	"strconv"
  15  
  16  	"golang.org/x/tools/internal/typeparams"
  17  	"golang.org/x/tools/internal/typesinternal"
  18  )
  19  
  20  // NewConst returns a new constant of the specified value and type.
  21  // val must be valid according to the specification of Const.Value.
  22  func NewConst(val constant.Value, typ types.Type) *Const {
  23  	if val == nil {
  24  		switch soleTypeKind(typ) {
  25  		case types.IsBoolean:
  26  			val = constant.MakeBool(false)
  27  		case types.IsInteger:
  28  			val = constant.MakeInt64(0)
  29  		case types.IsString:
  30  			val = constant.MakeString("")
  31  		}
  32  	}
  33  	return &Const{typ, val}
  34  }
  35  
  36  // soleTypeKind returns a BasicInfo for which constant.Value can
  37  // represent all zero values for the types in the type set.
  38  //
  39  //	types.IsBoolean for false is a representative.
  40  //	types.IsInteger for 0
  41  //	types.IsString for ""
  42  //	0 otherwise.
  43  func soleTypeKind(typ types.Type) types.BasicInfo {
  44  	// State records the set of possible zero values (false, 0, "").
  45  	// Candidates (perhaps all) are eliminated during the type-set
  46  	// iteration, which executes at least once.
  47  	state := types.IsBoolean | types.IsInteger | types.IsString
  48  	underIs(typ, func(ut types.Type) bool {
  49  		var c types.BasicInfo
  50  		if t, ok := ut.(*types.Basic); ok {
  51  			c = t.Info()
  52  		}
  53  		if c&types.IsNumeric != 0 { // int/float/complex
  54  			c = types.IsInteger
  55  		}
  56  		state = state & c
  57  		return state != 0
  58  	})
  59  	return state
  60  }
  61  
  62  // intConst returns an 'int' constant that evaluates to i.
  63  // (i is an int64 in case the host is narrower than the target.)
  64  func intConst(i int64) *Const {
  65  	return NewConst(constant.MakeInt64(i), tInt)
  66  }
  67  
  68  // stringConst returns a 'string' constant that evaluates to s.
  69  func stringConst(s string) *Const {
  70  	return NewConst(constant.MakeString(s), tString)
  71  }
  72  
  73  // zeroConst returns a new "zero" constant of the specified type.
  74  func zeroConst(t types.Type) *Const {
  75  	return NewConst(nil, t)
  76  }
  77  
  78  func (c *Const) RelString(from *types.Package) string {
  79  	var s string
  80  	if c.Value == nil {
  81  		s, _ = typesinternal.ZeroString(c.typ, types.RelativeTo(from))
  82  	} else if c.Value.Kind() == constant.String {
  83  		s = constant.StringVal(c.Value)
  84  		const max = 20
  85  		// TODO(adonovan): don't cut a rune in half.
  86  		if len(s) > max {
  87  			s = s[:max-3] + "..." // abbreviate
  88  		}
  89  		s = strconv.Quote(s)
  90  	} else {
  91  		s = c.Value.String()
  92  	}
  93  	return s + ":" + relType(c.Type(), from)
  94  }
  95  
  96  func (c *Const) Name() string {
  97  	return c.RelString(nil)
  98  }
  99  
 100  func (c *Const) String() string {
 101  	return c.Name()
 102  }
 103  
 104  func (c *Const) Type() types.Type {
 105  	return c.typ
 106  }
 107  
 108  func (c *Const) Referrers() *[]Instruction {
 109  	return nil
 110  }
 111  
 112  func (c *Const) Parent() *Function { return nil }
 113  
 114  func (c *Const) Pos() token.Pos {
 115  	return token.NoPos
 116  }
 117  
 118  // IsNil returns true if this constant is a nil value of
 119  // a nillable reference type (pointer, slice, channel, map, or function),
 120  // a basic interface type, or
 121  // a type parameter all of whose possible instantiations are themselves nillable.
 122  func (c *Const) IsNil() bool {
 123  	return c.Value == nil && nillable(c.typ)
 124  }
 125  
 126  // nillable reports whether *new(T) == nil is legal for type T.
 127  func nillable(t types.Type) bool {
 128  	if typeparams.IsTypeParam(t) {
 129  		return underIs(t, func(u types.Type) bool {
 130  			// empty type set (u==nil) => any underlying types => not nillable
 131  			return u != nil && nillable(u)
 132  		})
 133  	}
 134  	switch t.Underlying().(type) {
 135  	case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
 136  		return true
 137  	case *types.Interface:
 138  		return true // basic interface.
 139  	default:
 140  		return false
 141  	}
 142  }
 143  
 144  // TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
 145  
 146  // Int64 returns the numeric value of this constant truncated to fit
 147  // a signed 64-bit integer.
 148  func (c *Const) Int64() int64 {
 149  	switch x := constant.ToInt(c.Value); x.Kind() {
 150  	case constant.Int:
 151  		if i, ok := constant.Int64Val(x); ok {
 152  			return i
 153  		}
 154  		return 0
 155  	case constant.Float:
 156  		f, _ := constant.Float64Val(x)
 157  		return int64(f)
 158  	}
 159  	panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
 160  }
 161  
 162  // Uint64 returns the numeric value of this constant truncated to fit
 163  // an unsigned 64-bit integer.
 164  func (c *Const) Uint64() uint64 {
 165  	switch x := constant.ToInt(c.Value); x.Kind() {
 166  	case constant.Int:
 167  		if u, ok := constant.Uint64Val(x); ok {
 168  			return u
 169  		}
 170  		return 0
 171  	case constant.Float:
 172  		f, _ := constant.Float64Val(x)
 173  		return uint64(f)
 174  	}
 175  	panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
 176  }
 177  
 178  // Float64 returns the numeric value of this constant truncated to fit
 179  // a float64.
 180  func (c *Const) Float64() float64 {
 181  	x := constant.ToFloat(c.Value) // (c.Value == nil) => x.Kind() == Unknown
 182  	f, _ := constant.Float64Val(x)
 183  	return f
 184  }
 185  
 186  // Complex128 returns the complex value of this constant truncated to
 187  // fit a complex128.
 188  func (c *Const) Complex128() complex128 {
 189  	x := constant.ToComplex(c.Value) // (c.Value == nil) => x.Kind() == Unknown
 190  	re, _ := constant.Float64Val(constant.Real(x))
 191  	im, _ := constant.Float64Val(constant.Imag(x))
 192  	return complex(re, im)
 193  }
 194