golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/const.go (about) 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 "strings" 16 17 "golang.org/x/tools/internal/aliases" 18 "golang.org/x/tools/internal/typeparams" 19 ) 20 21 // NewConst returns a new constant of the specified value and type. 22 // val must be valid according to the specification of Const.Value. 23 func NewConst(val constant.Value, typ types.Type) *Const { 24 if val == nil { 25 switch soleTypeKind(typ) { 26 case types.IsBoolean: 27 val = constant.MakeBool(false) 28 case types.IsInteger: 29 val = constant.MakeInt64(0) 30 case types.IsString: 31 val = constant.MakeString("") 32 } 33 } 34 return &Const{typ, val} 35 } 36 37 // soleTypeKind returns a BasicInfo for which constant.Value can 38 // represent all zero values for the types in the type set. 39 // 40 // types.IsBoolean for false is a representative. 41 // types.IsInteger for 0 42 // types.IsString for "" 43 // 0 otherwise. 44 func soleTypeKind(typ types.Type) types.BasicInfo { 45 // State records the set of possible zero values (false, 0, ""). 46 // Candidates (perhaps all) are eliminated during the type-set 47 // iteration, which executes at least once. 48 state := types.IsBoolean | types.IsInteger | types.IsString 49 underIs(typeSetOf(typ), func(ut types.Type) bool { 50 var c types.BasicInfo 51 if t, ok := ut.(*types.Basic); ok { 52 c = t.Info() 53 } 54 if c&types.IsNumeric != 0 { // int/float/complex 55 c = types.IsInteger 56 } 57 state = state & c 58 return state != 0 59 }) 60 return state 61 } 62 63 // intConst returns an 'int' constant that evaluates to i. 64 // (i is an int64 in case the host is narrower than the target.) 65 func intConst(i int64) *Const { 66 return NewConst(constant.MakeInt64(i), tInt) 67 } 68 69 // stringConst returns a 'string' constant that evaluates to s. 70 func stringConst(s string) *Const { 71 return NewConst(constant.MakeString(s), tString) 72 } 73 74 // zeroConst returns a new "zero" constant of the specified type. 75 func zeroConst(t types.Type) *Const { 76 return NewConst(nil, t) 77 } 78 79 func (c *Const) RelString(from *types.Package) string { 80 var s string 81 if c.Value == nil { 82 s = zeroString(c.typ, from) 83 } else if c.Value.Kind() == constant.String { 84 s = constant.StringVal(c.Value) 85 const max = 20 86 // TODO(adonovan): don't cut a rune in half. 87 if len(s) > max { 88 s = s[:max-3] + "..." // abbreviate 89 } 90 s = strconv.Quote(s) 91 } else { 92 s = c.Value.String() 93 } 94 return s + ":" + relType(c.Type(), from) 95 } 96 97 // zeroString returns the string representation of the "zero" value of the type t. 98 func zeroString(t types.Type, from *types.Package) string { 99 switch t := t.(type) { 100 case *types.Basic: 101 switch { 102 case t.Info()&types.IsBoolean != 0: 103 return "false" 104 case t.Info()&types.IsNumeric != 0: 105 return "0" 106 case t.Info()&types.IsString != 0: 107 return `""` 108 case t.Kind() == types.UnsafePointer: 109 fallthrough 110 case t.Kind() == types.UntypedNil: 111 return "nil" 112 default: 113 panic(fmt.Sprint("zeroString for unexpected type:", t)) 114 } 115 case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: 116 return "nil" 117 case *types.Named, *aliases.Alias: 118 return zeroString(t.Underlying(), from) 119 case *types.Array, *types.Struct: 120 return relType(t, from) + "{}" 121 case *types.Tuple: 122 // Tuples are not normal values. 123 // We are currently format as "(t[0], ..., t[n])". Could be something else. 124 components := make([]string, t.Len()) 125 for i := 0; i < t.Len(); i++ { 126 components[i] = zeroString(t.At(i).Type(), from) 127 } 128 return "(" + strings.Join(components, ", ") + ")" 129 case *types.TypeParam: 130 return "*new(" + relType(t, from) + ")" 131 } 132 panic(fmt.Sprint("zeroString: unexpected ", t)) 133 } 134 135 func (c *Const) Name() string { 136 return c.RelString(nil) 137 } 138 139 func (c *Const) String() string { 140 return c.Name() 141 } 142 143 func (c *Const) Type() types.Type { 144 return c.typ 145 } 146 147 func (c *Const) Referrers() *[]Instruction { 148 return nil 149 } 150 151 func (c *Const) Parent() *Function { return nil } 152 153 func (c *Const) Pos() token.Pos { 154 return token.NoPos 155 } 156 157 // IsNil returns true if this constant is a nil value of 158 // a nillable reference type (pointer, slice, channel, map, or function), 159 // a basic interface type, or 160 // a type parameter all of whose possible instantiations are themselves nillable. 161 func (c *Const) IsNil() bool { 162 return c.Value == nil && nillable(c.typ) 163 } 164 165 // nillable reports whether *new(T) == nil is legal for type T. 166 func nillable(t types.Type) bool { 167 if typeparams.IsTypeParam(t) { 168 return underIs(typeSetOf(t), func(u types.Type) bool { 169 // empty type set (u==nil) => any underlying types => not nillable 170 return u != nil && nillable(u) 171 }) 172 } 173 switch t.Underlying().(type) { 174 case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: 175 return true 176 case *types.Interface: 177 return true // basic interface. 178 default: 179 return false 180 } 181 } 182 183 // TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp. 184 185 // Int64 returns the numeric value of this constant truncated to fit 186 // a signed 64-bit integer. 187 func (c *Const) Int64() int64 { 188 switch x := constant.ToInt(c.Value); x.Kind() { 189 case constant.Int: 190 if i, ok := constant.Int64Val(x); ok { 191 return i 192 } 193 return 0 194 case constant.Float: 195 f, _ := constant.Float64Val(x) 196 return int64(f) 197 } 198 panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) 199 } 200 201 // Uint64 returns the numeric value of this constant truncated to fit 202 // an unsigned 64-bit integer. 203 func (c *Const) Uint64() uint64 { 204 switch x := constant.ToInt(c.Value); x.Kind() { 205 case constant.Int: 206 if u, ok := constant.Uint64Val(x); ok { 207 return u 208 } 209 return 0 210 case constant.Float: 211 f, _ := constant.Float64Val(x) 212 return uint64(f) 213 } 214 panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) 215 } 216 217 // Float64 returns the numeric value of this constant truncated to fit 218 // a float64. 219 func (c *Const) Float64() float64 { 220 x := constant.ToFloat(c.Value) // (c.Value == nil) => x.Kind() == Unknown 221 f, _ := constant.Float64Val(x) 222 return f 223 } 224 225 // Complex128 returns the complex value of this constant truncated to 226 // fit a complex128. 227 func (c *Const) Complex128() complex128 { 228 x := constant.ToComplex(c.Value) // (c.Value == nil) => x.Kind() == Unknown 229 re, _ := constant.Float64Val(constant.Real(x)) 230 im, _ := constant.Float64Val(constant.Imag(x)) 231 return complex(re, im) 232 }