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 int32 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  	case *SelectorExpr:
 139  		pkgName, ok := e.X.(*Name)
 140  		if !ok {
 141  			return nil
 142  		}
 143  		_, obj := c.lookup(pkgName.Value, c.pkg.scope)
 144  		if obj == nil {
 145  			return nil
 146  		}
 147  		pkgObj, ok := obj.(*PkgName)
 148  		if !ok {
 149  			return nil
 150  		}
 151  		if pkgObj.imported == nil {
 152  			return nil
 153  		}
 154  		member := pkgObj.imported.scope.Lookup(e.Sel.Value)
 155  		if member == nil {
 156  			return nil
 157  		}
 158  		if k, ok := member.(*TCConst); ok {
 159  			if cv, ok := k.val.(constant.Value); ok {
 160  				return cv
 161  			}
 162  		}
 163  		return nil
 164  	}
 165  	return nil
 166  }
 167  
 168  func evalBasicLit(e *BasicLit) constant.Value {
 169  	switch e.Kind {
 170  	case IntLit:
 171  		return constant.MakeFromLiteral(e.Value, token.INT, 0)
 172  	case FloatLit:
 173  		return constant.MakeFromLiteral(e.Value, token.FLOAT, 0)
 174  	case StringLit:
 175  		return constant.MakeFromLiteral(e.Value, token.STRING, 0)
 176  	case RuneLit:
 177  		return constant.MakeFromLiteral(e.Value, token.CHAR, 0)
 178  	}
 179  	return nil
 180  }
 181  
 182  func (c *Checker) evalConstOp(e *Operation, iotaVal int64) constant.Value {
 183  	if e.Y == nil {
 184  		// unary
 185  		x := c.evalConst(e.X, iotaVal)
 186  		if x == nil {
 187  			return nil
 188  		}
 189  		op := syntaxOpToToken(e.Op)
 190  		if op == token.ILLEGAL {
 191  			return nil
 192  		}
 193  		return constant.UnaryOp(op, x, 0)
 194  	}
 195  	// binary
 196  	x := c.evalConst(e.X, iotaVal)
 197  	y := c.evalConst(e.Y, iotaVal)
 198  	if x == nil || y == nil {
 199  		return nil
 200  	}
 201  	op := syntaxOpToToken(e.Op)
 202  	if op == token.ILLEGAL {
 203  		return nil
 204  	}
 205  	if op == token.SHL || op == token.SHR {
 206  		shift, ok := constant.Uint64Val(y)
 207  		if !ok {
 208  			return nil
 209  		}
 210  		return constant.Shift(x, op, uint32(shift))
 211  	}
 212  	// Moxie: | on string constants is concat. The syntax AST has Or (|) because
 213  	// rewriteConstPipes only runs on the go/ast side, not on syntax.File.
 214  	if op == token.OR && x.Kind() == constant.String && y.Kind() == constant.String {
 215  		xs := constant.StringVal(x)
 216  		ys := constant.StringVal(y)
 217  		return constant.MakeString(xs | ys)
 218  	}
 219  	// Comparison operators must use constant.Compare, not BinaryOp.
 220  	switch op {
 221  	case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
 222  		return constant.MakeBool(constant.Compare(x, op, y))
 223  	}
 224  	return constant.BinaryOp(x, op, y)
 225  }
 226  
 227  func syntaxOpToToken(op Operator) token.Token {
 228  	switch op {
 229  	case Add:
 230  		return token.ADD
 231  	case Sub:
 232  		return token.SUB
 233  	case Mul:
 234  		return token.MUL
 235  	case Div:
 236  		return token.QUO
 237  	case Rem:
 238  		return token.REM
 239  	case And:
 240  		return token.AND
 241  	case Or:
 242  		return token.OR
 243  	case Xor:
 244  		return token.XOR
 245  	case Shl:
 246  		return token.SHL
 247  	case Shr:
 248  		return token.SHR
 249  	case AndNot:
 250  		return token.AND_NOT
 251  	case Not:
 252  		return token.NOT
 253  	case Eql:
 254  		return token.EQL
 255  	case Neq:
 256  		return token.NEQ
 257  	case Lss:
 258  		return token.LSS
 259  	case Leq:
 260  		return token.LEQ
 261  	case Gtr:
 262  		return token.GTR
 263  	case Geq:
 264  		return token.GEQ
 265  	}
 266  	return token.ILLEGAL
 267  }
 268  
 269  // convertConst converts a constant value to the representation expected by
 270  // a given target type. Handles explicit type conversions in const exprs.
 271  func convertConst(v constant.Value, target Type) constant.Value {
 272  	b, ok := target.Underlying().(*Basic)
 273  	if !ok {
 274  		return v
 275  	}
 276  	switch {
 277  	case b.info&IsInteger != 0:
 278  		i, _ := constant.Int64Val(v)
 279  		return constant.MakeInt64(i)
 280  	case b.info&IsFloat != 0:
 281  		f, _ := constant.Float64Val(v)
 282  		return constant.MakeFloat64(f)
 283  	case b.info&IsString != 0:
 284  		if v.Kind() == constant.Int {
 285  			n, _ := constant.Uint64Val(v)
 286  			r := rune(n)
 287  			return constant.MakeString(string(r))
 288  		}
 289  		return v
 290  	}
 291  	return v
 292  }
 293  
 294  // untypedTypeOf returns the untyped type for a constant value.
 295  func untypedTypeOf(v constant.Value) Type {
 296  	switch v.Kind() {
 297  	case constant.Bool:
 298  		return Typ[UntypedBool]
 299  	case constant.Int:
 300  		return Typ[UntypedInt]
 301  	case constant.Float:
 302  		return Typ[UntypedFloat]
 303  	case constant.String:
 304  		return Typ[UntypedString]
 305  	}
 306  	return nil
 307  }
 308  
 309  // constInt64 extracts the int64 value of a constant, used for array lengths etc.
 310  // Returns 0 and false if not a constant integer.
 311  func constInt64(v constant.Value) (int64, bool) {
 312  	if v == nil || v.Kind() != constant.Int {
 313  		return 0, false
 314  	}
 315  	n, exact := constant.Int64Val(v)
 316  	return n, exact
 317  }
 318  
 319  // IsConstExpr reports whether e is a constant expression that can be evaluated
 320  // at compile time (used to gate array length resolution).
 321  func (c *Checker) IsConstExpr(e Expr) bool {
 322  	return c.evalConst(e, 0) != nil
 323  }
 324  
 325  // evalArrayLen evaluates a constant array length expression.
 326  func (c *Checker) evalArrayLen(e Expr) int64 {
 327  	v := c.evalConst(e, 0)
 328  	if v == nil {
 329  		return 0
 330  	}
 331  	n, _ := constInt64(v)
 332  	return n
 333  }
 334  
 335  // interpString converts a Go string literal value to a Go string.
 336  // Handles quoted forms: "...", `...`, '...'.
 337  func interpString(s string) string {
 338  	if len(s) < 2 {
 339  		return s
 340  	}
 341  	if s[0] == '`' {
 342  		return s[1 : len(s)-1]
 343  	}
 344  	unquoted, err := strconv.Unquote(s)
 345  	if err != nil {
 346  		return bytes.Trim(s, `"`)
 347  	}
 348  	return unquoted
 349  }
 350  
 351  // bigIntVal extracts a *big.Int from a constant for use in overflow checks.
 352  func bigIntVal(v constant.Value) *big.Int {
 353  	if v.Kind() != constant.Int {
 354  		return nil
 355  	}
 356  	b := &big.Int{}
 357  	s := v.ExactString()
 358  	b.SetString(s, 10)
 359  	return b
 360  }
 361