const.go raw

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