tc_const.mx raw

   1  package main
   2  
   3  import (
   4  	"go/constant"
   5  	"go/token"
   6  	"math/big"
   7  	"bytes"
   8  	"strconv"
   9  )
  10  
  11  // checkConstGroup processes a sequence of ConstDecls that share a Group.
  12  // iota advances per ConstDecl position. Inherited type/value expressions
  13  // carry forward when a ConstDecl has no explicit values (standard Go rule).
  14  func (c *Checker) checkConstGroup(decls []*ConstDecl) {
  15  	var prevType Expr
  16  	var prevValues Expr
  17  	for i, d := range decls {
  18  		typ := d.Type
  19  		vals := d.Values
  20  		if vals == nil {
  21  			// inherit from previous spec
  22  			typ = prevType
  23  			vals = prevValues
  24  		} else {
  25  			prevType = typ
  26  			prevValues = vals
  27  		}
  28  		c.checkConstDeclAt(d, typ, vals, int64(i))
  29  	}
  30  }
  31  
  32  func (c *Checker) checkConstDeclAt(d *ConstDecl, typExpr Expr, valExpr Expr, iotaVal int64) {
  33  	var typ Type
  34  	if typExpr != nil {
  35  		typ = c.resolveTypeExpr(typExpr)
  36  	}
  37  
  38  	var vals []constant.Value
  39  	if valExpr != nil {
  40  		vals = c.evalConstExprList(valExpr, iotaVal)
  41  	}
  42  
  43  	for i, name := range d.NameList {
  44  		if name.Value == "_" {
  45  			continue
  46  		}
  47  		obj, ok := c.pkg.scope.Lookup(name.Value).(*TCConst)
  48  		if !ok {
  49  			continue
  50  		}
  51  		var cv constant.Value
  52  		if i < len(vals) {
  53  			cv = vals[i]
  54  		}
  55  		obj.val = cv
  56  		if typ != nil {
  57  			obj.typ = typ
  58  		} else if cv != nil {
  59  			obj.typ = untypedTypeOf(cv)
  60  		}
  61  		if c.info != nil {
  62  			c.info.Defs[name] = obj
  63  		}
  64  	}
  65  }
  66  
  67  // evalConstExprList evaluates a (possibly multi-value) const expression.
  68  func (c *Checker) evalConstExprList(e Expr, iotaVal int64) []constant.Value {
  69  	if l, ok := e.(*ListExpr); ok {
  70  		var out []constant.Value
  71  		for _, el := range l.ElemList {
  72  			v := c.evalConst(el, iotaVal)
  73  			out = append(out, v)
  74  		}
  75  		return out
  76  	}
  77  	v := c.evalConst(e, iotaVal)
  78  	return []constant.Value{v}
  79  }
  80  
  81  // evalConst evaluates a constant expression, returning a constant.Value.
  82  // Returns nil if the expression is not a constant.
  83  func (c *Checker) evalConst(e Expr, iotaVal int64) constant.Value {
  84  	if e == nil {
  85  		return nil
  86  	}
  87  	switch e := e.(type) {
  88  	case *BasicLit:
  89  		return evalBasicLit(e)
  90  	case *Name:
  91  		if e.Value == "iota" {
  92  			return constant.MakeInt64(iotaVal)
  93  		}
  94  		_, obj := c.lookup(e.Value, c.pkg.scope)
  95  		if obj == nil {
  96  			return nil
  97  		}
  98  		if k, ok := obj.(*TCConst); ok {
  99  			if cv, ok := k.val.(constant.Value); ok {
 100  				return cv
 101  			}
 102  		}
 103  		return nil
 104  	case *Operation:
 105  		return c.evalConstOp(e, iotaVal)
 106  	case *ParenExpr:
 107  		return c.evalConst(e.X, iotaVal)
 108  	case *CallExpr:
 109  		// len("string literal") is a constant in Go.
 110  		if id, ok := e.Fun.(*Name); ok && id.Value == "len" && len(e.ArgList) == 1 {
 111  			if lit, ok := e.ArgList[0].(*BasicLit); ok && lit.Kind == StringLit {
 112  				s := constant.StringVal(evalBasicLit(lit))
 113  				return constant.MakeInt64(int64(len(s)))
 114  			}
 115  		}
 116  		// unsafe.Sizeof/Alignof/Offsetof - return a placeholder int constant.
 117  		if sel, ok := e.Fun.(*SelectorExpr); ok {
 118  			if pkg, ok := sel.X.(*Name); ok && pkg.Value == "unsafe" {
 119  				switch sel.Sel.Value {
 120  				case "Sizeof", "Alignof", "Offsetof":
 121  					return constant.MakeInt64(8) // conservative placeholder (pointer size)
 122  				}
 123  			}
 124  		}
 125  		// Type conversion of constant: T(x).
 126  		if len(e.ArgList) != 1 {
 127  			return nil
 128  		}
 129  		inner := c.evalConst(e.ArgList[0], iotaVal)
 130  		if inner == nil {
 131  			return nil
 132  		}
 133  		targetType := c.resolveTypeExpr(e.Fun)
 134  		if targetType == nil {
 135  			return nil
 136  		}
 137  		return convertConst(inner, targetType)
 138  	}
 139  	return nil
 140  }
 141  
 142  func evalBasicLit(e *BasicLit) constant.Value {
 143  	switch e.Kind {
 144  	case IntLit:
 145  		return constant.MakeFromLiteral(e.Value, token.INT, 0)
 146  	case FloatLit:
 147  		return constant.MakeFromLiteral(e.Value, token.FLOAT, 0)
 148  	case StringLit:
 149  		return constant.MakeFromLiteral(e.Value, token.STRING, 0)
 150  	case RuneLit:
 151  		return constant.MakeFromLiteral(e.Value, token.CHAR, 0)
 152  	}
 153  	return nil
 154  }
 155  
 156  func (c *Checker) evalConstOp(e *Operation, iotaVal int64) constant.Value {
 157  	if e.Y == nil {
 158  		// unary
 159  		x := c.evalConst(e.X, iotaVal)
 160  		if x == nil {
 161  			return nil
 162  		}
 163  		op := syntaxOpToToken(e.Op)
 164  		if op == token.ILLEGAL {
 165  			return nil
 166  		}
 167  		return constant.UnaryOp(op, x, 0)
 168  	}
 169  	// binary
 170  	x := c.evalConst(e.X, iotaVal)
 171  	y := c.evalConst(e.Y, iotaVal)
 172  	if x == nil || y == nil {
 173  		return nil
 174  	}
 175  	op := syntaxOpToToken(e.Op)
 176  	if op == token.ILLEGAL {
 177  		return nil
 178  	}
 179  	if op == token.SHL || op == token.SHR {
 180  		shift, ok := constant.Uint64Val(y)
 181  		if !ok {
 182  			return nil
 183  		}
 184  		return constant.Shift(x, op, uint(shift))
 185  	}
 186  	// Moxie: | on string constants is concat. The syntax AST has Or (|) because
 187  	// rewriteConstPipes only runs on the go/ast side, not on syntax.File.
 188  	if op == token.OR && x.Kind() == constant.String && y.Kind() == constant.String {
 189  		xs := constant.StringVal(x)
 190  		ys := constant.StringVal(y)
 191  		return constant.MakeString(xs | ys)
 192  	}
 193  	// Comparison operators must use constant.Compare, not BinaryOp.
 194  	switch op {
 195  	case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
 196  		return constant.MakeBool(constant.Compare(x, op, y))
 197  	}
 198  	return constant.BinaryOp(x, op, y)
 199  }
 200  
 201  func syntaxOpToToken(op Operator) token.Token {
 202  	switch op {
 203  	case Add:
 204  		return token.ADD
 205  	case Sub:
 206  		return token.SUB
 207  	case Mul:
 208  		return token.MUL
 209  	case Div:
 210  		return token.QUO
 211  	case Rem:
 212  		return token.REM
 213  	case And:
 214  		return token.AND
 215  	case Or:
 216  		return token.OR
 217  	case Xor:
 218  		return token.XOR
 219  	case Shl:
 220  		return token.SHL
 221  	case Shr:
 222  		return token.SHR
 223  	case AndNot:
 224  		return token.AND_NOT
 225  	case Not:
 226  		return token.NOT
 227  	case Eql:
 228  		return token.EQL
 229  	case Neq:
 230  		return token.NEQ
 231  	case Lss:
 232  		return token.LSS
 233  	case Leq:
 234  		return token.LEQ
 235  	case Gtr:
 236  		return token.GTR
 237  	case Geq:
 238  		return token.GEQ
 239  	}
 240  	return token.ILLEGAL
 241  }
 242  
 243  // convertConst converts a constant value to the representation expected by
 244  // a given target type. Handles explicit type conversions in const exprs.
 245  func convertConst(v constant.Value, target Type) constant.Value {
 246  	b, ok := safeUnderlying(target).(*Basic)
 247  	if !ok {
 248  		return v
 249  	}
 250  	switch {
 251  	case b.info&IsInteger != 0:
 252  		i, _ := constant.Int64Val(v)
 253  		return constant.MakeInt64(i)
 254  	case b.info&IsFloat != 0:
 255  		f, _ := constant.Float64Val(v)
 256  		return constant.MakeFloat64(f)
 257  	case b.info&IsString != 0:
 258  		if v.Kind() == constant.Int {
 259  			n, _ := constant.Uint64Val(v)
 260  			r := rune(n)
 261  			return constant.MakeString(string(r))
 262  		}
 263  		return v
 264  	}
 265  	return v
 266  }
 267  
 268  // untypedTypeOf returns the untyped type for a constant value.
 269  func untypedTypeOf(v constant.Value) Type {
 270  	switch v.Kind() {
 271  	case constant.Bool:
 272  		return Typ[UntypedBool]
 273  	case constant.Int:
 274  		return Typ[UntypedInt]
 275  	case constant.Float:
 276  		return Typ[UntypedFloat]
 277  	case constant.String:
 278  		return Typ[UntypedString]
 279  	}
 280  	return nil
 281  }
 282  
 283  // constInt64 extracts the int64 value of a constant, used for array lengths etc.
 284  // Returns 0 and false if not a constant integer.
 285  func constInt64(v constant.Value) (int64, bool) {
 286  	if v == nil || v.Kind() != constant.Int {
 287  		return 0, false
 288  	}
 289  	n, exact := constant.Int64Val(v)
 290  	return n, exact
 291  }
 292  
 293  // IsConstExpr reports whether e is a constant expression that can be evaluated
 294  // at compile time (used to gate array length resolution).
 295  func (c *Checker) IsConstExpr(e Expr) bool {
 296  	return c.evalConst(e, 0) != nil
 297  }
 298  
 299  // evalArrayLen evaluates a constant array length expression.
 300  func (c *Checker) evalArrayLen(e Expr) int64 {
 301  	v := c.evalConst(e, 0)
 302  	if v == nil {
 303  		return 0
 304  	}
 305  	n, _ := constInt64(v)
 306  	return n
 307  }
 308  
 309  // interpString converts a Go string literal value to a Go string.
 310  // Handles quoted forms: "...", `...`, '...'.
 311  func interpString(s string) string {
 312  	if len(s) < 2 {
 313  		return s
 314  	}
 315  	if s[0] == '`' {
 316  		return s[1 : len(s)-1]
 317  	}
 318  	unquoted, err := strconv.Unquote(s)
 319  	if err != nil {
 320  		return bytes.Trim(s, `"`)
 321  	}
 322  	return unquoted
 323  }
 324  
 325  // bigIntVal extracts a *big.Int from a constant for use in overflow checks.
 326  func bigIntVal(v constant.Value) *big.Int {
 327  	if v.Kind() != constant.Int {
 328  		return nil
 329  	}
 330  	b := &big.Int{}
 331  	s := v.ExactString()
 332  	b.SetString(s, 10)
 333  	return b
 334  }
 335