github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/staticcheck/vrp/int.go (about)

     1  package vrp
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"go/types"
     7  	"math/big"
     8  
     9  	"github.com/golangci/go-tools/ssa"
    10  )
    11  
    12  type Zs []Z
    13  
    14  func (zs Zs) Len() int {
    15  	return len(zs)
    16  }
    17  
    18  func (zs Zs) Less(i int, j int) bool {
    19  	return zs[i].Cmp(zs[j]) == -1
    20  }
    21  
    22  func (zs Zs) Swap(i int, j int) {
    23  	zs[i], zs[j] = zs[j], zs[i]
    24  }
    25  
    26  type Z struct {
    27  	infinity int8
    28  	integer  *big.Int
    29  }
    30  
    31  func NewZ(n int64) Z {
    32  	return NewBigZ(big.NewInt(n))
    33  }
    34  
    35  func NewBigZ(n *big.Int) Z {
    36  	return Z{integer: n}
    37  }
    38  
    39  func (z1 Z) Infinite() bool {
    40  	return z1.infinity != 0
    41  }
    42  
    43  func (z1 Z) Add(z2 Z) Z {
    44  	if z2.Sign() == -1 {
    45  		return z1.Sub(z2.Negate())
    46  	}
    47  	if z1 == NInfinity {
    48  		return NInfinity
    49  	}
    50  	if z1 == PInfinity {
    51  		return PInfinity
    52  	}
    53  	if z2 == PInfinity {
    54  		return PInfinity
    55  	}
    56  
    57  	if !z1.Infinite() && !z2.Infinite() {
    58  		n := &big.Int{}
    59  		n.Add(z1.integer, z2.integer)
    60  		return NewBigZ(n)
    61  	}
    62  
    63  	panic(fmt.Sprintf("%s + %s is not defined", z1, z2))
    64  }
    65  
    66  func (z1 Z) Sub(z2 Z) Z {
    67  	if z2.Sign() == -1 {
    68  		return z1.Add(z2.Negate())
    69  	}
    70  	if !z1.Infinite() && !z2.Infinite() {
    71  		n := &big.Int{}
    72  		n.Sub(z1.integer, z2.integer)
    73  		return NewBigZ(n)
    74  	}
    75  
    76  	if z1 != PInfinity && z2 == PInfinity {
    77  		return NInfinity
    78  	}
    79  	if z1.Infinite() && !z2.Infinite() {
    80  		return Z{infinity: z1.infinity}
    81  	}
    82  	if z1 == PInfinity && z2 == PInfinity {
    83  		return PInfinity
    84  	}
    85  	panic(fmt.Sprintf("%s - %s is not defined", z1, z2))
    86  }
    87  
    88  func (z1 Z) Mul(z2 Z) Z {
    89  	if (z1.integer != nil && z1.integer.Sign() == 0) ||
    90  		(z2.integer != nil && z2.integer.Sign() == 0) {
    91  		return NewBigZ(&big.Int{})
    92  	}
    93  
    94  	if z1.infinity != 0 || z2.infinity != 0 {
    95  		return Z{infinity: int8(z1.Sign() * z2.Sign())}
    96  	}
    97  
    98  	n := &big.Int{}
    99  	n.Mul(z1.integer, z2.integer)
   100  	return NewBigZ(n)
   101  }
   102  
   103  func (z1 Z) Negate() Z {
   104  	if z1.infinity == 1 {
   105  		return NInfinity
   106  	}
   107  	if z1.infinity == -1 {
   108  		return PInfinity
   109  	}
   110  	n := &big.Int{}
   111  	n.Neg(z1.integer)
   112  	return NewBigZ(n)
   113  }
   114  
   115  func (z1 Z) Sign() int {
   116  	if z1.infinity != 0 {
   117  		return int(z1.infinity)
   118  	}
   119  	return z1.integer.Sign()
   120  }
   121  
   122  func (z1 Z) String() string {
   123  	if z1 == NInfinity {
   124  		return "-∞"
   125  	}
   126  	if z1 == PInfinity {
   127  		return "∞"
   128  	}
   129  	return fmt.Sprintf("%d", z1.integer)
   130  }
   131  
   132  func (z1 Z) Cmp(z2 Z) int {
   133  	if z1.infinity == z2.infinity && z1.infinity != 0 {
   134  		return 0
   135  	}
   136  	if z1 == PInfinity {
   137  		return 1
   138  	}
   139  	if z1 == NInfinity {
   140  		return -1
   141  	}
   142  	if z2 == NInfinity {
   143  		return 1
   144  	}
   145  	if z2 == PInfinity {
   146  		return -1
   147  	}
   148  	return z1.integer.Cmp(z2.integer)
   149  }
   150  
   151  func MaxZ(zs ...Z) Z {
   152  	if len(zs) == 0 {
   153  		panic("Max called with no arguments")
   154  	}
   155  	if len(zs) == 1 {
   156  		return zs[0]
   157  	}
   158  	ret := zs[0]
   159  	for _, z := range zs[1:] {
   160  		if z.Cmp(ret) == 1 {
   161  			ret = z
   162  		}
   163  	}
   164  	return ret
   165  }
   166  
   167  func MinZ(zs ...Z) Z {
   168  	if len(zs) == 0 {
   169  		panic("Min called with no arguments")
   170  	}
   171  	if len(zs) == 1 {
   172  		return zs[0]
   173  	}
   174  	ret := zs[0]
   175  	for _, z := range zs[1:] {
   176  		if z.Cmp(ret) == -1 {
   177  			ret = z
   178  		}
   179  	}
   180  	return ret
   181  }
   182  
   183  var NInfinity = Z{infinity: -1}
   184  var PInfinity = Z{infinity: 1}
   185  var EmptyIntInterval = IntInterval{true, PInfinity, NInfinity}
   186  
   187  func InfinityFor(v ssa.Value) IntInterval {
   188  	if b, ok := v.Type().Underlying().(*types.Basic); ok {
   189  		if (b.Info() & types.IsUnsigned) != 0 {
   190  			return NewIntInterval(NewZ(0), PInfinity)
   191  		}
   192  	}
   193  	return NewIntInterval(NInfinity, PInfinity)
   194  }
   195  
   196  type IntInterval struct {
   197  	known bool
   198  	Lower Z
   199  	Upper Z
   200  }
   201  
   202  func NewIntInterval(l, u Z) IntInterval {
   203  	if u.Cmp(l) == -1 {
   204  		return EmptyIntInterval
   205  	}
   206  	return IntInterval{known: true, Lower: l, Upper: u}
   207  }
   208  
   209  func (i IntInterval) IsKnown() bool {
   210  	return i.known
   211  }
   212  
   213  func (i IntInterval) Empty() bool {
   214  	return i.Lower == PInfinity && i.Upper == NInfinity
   215  }
   216  
   217  func (i IntInterval) IsMaxRange() bool {
   218  	return i.Lower == NInfinity && i.Upper == PInfinity
   219  }
   220  
   221  func (i1 IntInterval) Intersection(i2 IntInterval) IntInterval {
   222  	if !i1.IsKnown() {
   223  		return i2
   224  	}
   225  	if !i2.IsKnown() {
   226  		return i1
   227  	}
   228  	if i1.Empty() || i2.Empty() {
   229  		return EmptyIntInterval
   230  	}
   231  	i3 := NewIntInterval(MaxZ(i1.Lower, i2.Lower), MinZ(i1.Upper, i2.Upper))
   232  	if i3.Lower.Cmp(i3.Upper) == 1 {
   233  		return EmptyIntInterval
   234  	}
   235  	return i3
   236  }
   237  
   238  func (i1 IntInterval) Union(other Range) Range {
   239  	i2, ok := other.(IntInterval)
   240  	if !ok {
   241  		i2 = EmptyIntInterval
   242  	}
   243  	if i1.Empty() || !i1.IsKnown() {
   244  		return i2
   245  	}
   246  	if i2.Empty() || !i2.IsKnown() {
   247  		return i1
   248  	}
   249  	return NewIntInterval(MinZ(i1.Lower, i2.Lower), MaxZ(i1.Upper, i2.Upper))
   250  }
   251  
   252  func (i1 IntInterval) Add(i2 IntInterval) IntInterval {
   253  	if i1.Empty() || i2.Empty() {
   254  		return EmptyIntInterval
   255  	}
   256  	l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
   257  	return NewIntInterval(l1.Add(l2), u1.Add(u2))
   258  }
   259  
   260  func (i1 IntInterval) Sub(i2 IntInterval) IntInterval {
   261  	if i1.Empty() || i2.Empty() {
   262  		return EmptyIntInterval
   263  	}
   264  	l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
   265  	return NewIntInterval(l1.Sub(u2), u1.Sub(l2))
   266  }
   267  
   268  func (i1 IntInterval) Mul(i2 IntInterval) IntInterval {
   269  	if i1.Empty() || i2.Empty() {
   270  		return EmptyIntInterval
   271  	}
   272  	x1, x2 := i1.Lower, i1.Upper
   273  	y1, y2 := i2.Lower, i2.Upper
   274  	return NewIntInterval(
   275  		MinZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
   276  		MaxZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
   277  	)
   278  }
   279  
   280  func (i1 IntInterval) String() string {
   281  	if !i1.IsKnown() {
   282  		return "[⊥, ⊥]"
   283  	}
   284  	if i1.Empty() {
   285  		return "{}"
   286  	}
   287  	return fmt.Sprintf("[%s, %s]", i1.Lower, i1.Upper)
   288  }
   289  
   290  type IntArithmeticConstraint struct {
   291  	aConstraint
   292  	A  ssa.Value
   293  	B  ssa.Value
   294  	Op token.Token
   295  	Fn func(IntInterval, IntInterval) IntInterval
   296  }
   297  
   298  type IntAddConstraint struct{ *IntArithmeticConstraint }
   299  type IntSubConstraint struct{ *IntArithmeticConstraint }
   300  type IntMulConstraint struct{ *IntArithmeticConstraint }
   301  
   302  type IntConversionConstraint struct {
   303  	aConstraint
   304  	X ssa.Value
   305  }
   306  
   307  type IntIntersectionConstraint struct {
   308  	aConstraint
   309  	ranges   Ranges
   310  	A        ssa.Value
   311  	B        ssa.Value
   312  	Op       token.Token
   313  	I        IntInterval
   314  	resolved bool
   315  }
   316  
   317  type IntIntervalConstraint struct {
   318  	aConstraint
   319  	I IntInterval
   320  }
   321  
   322  func NewIntArithmeticConstraint(a, b, y ssa.Value, op token.Token, fn func(IntInterval, IntInterval) IntInterval) *IntArithmeticConstraint {
   323  	return &IntArithmeticConstraint{NewConstraint(y), a, b, op, fn}
   324  }
   325  func NewIntAddConstraint(a, b, y ssa.Value) Constraint {
   326  	return &IntAddConstraint{NewIntArithmeticConstraint(a, b, y, token.ADD, IntInterval.Add)}
   327  }
   328  func NewIntSubConstraint(a, b, y ssa.Value) Constraint {
   329  	return &IntSubConstraint{NewIntArithmeticConstraint(a, b, y, token.SUB, IntInterval.Sub)}
   330  }
   331  func NewIntMulConstraint(a, b, y ssa.Value) Constraint {
   332  	return &IntMulConstraint{NewIntArithmeticConstraint(a, b, y, token.MUL, IntInterval.Mul)}
   333  }
   334  func NewIntConversionConstraint(x, y ssa.Value) Constraint {
   335  	return &IntConversionConstraint{NewConstraint(y), x}
   336  }
   337  func NewIntIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
   338  	return &IntIntersectionConstraint{
   339  		aConstraint: NewConstraint(y),
   340  		ranges:      ranges,
   341  		A:           a,
   342  		B:           b,
   343  		Op:          op,
   344  	}
   345  }
   346  func NewIntIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
   347  	return &IntIntervalConstraint{NewConstraint(y), i}
   348  }
   349  
   350  func (c *IntArithmeticConstraint) Operands() []ssa.Value   { return []ssa.Value{c.A, c.B} }
   351  func (c *IntConversionConstraint) Operands() []ssa.Value   { return []ssa.Value{c.X} }
   352  func (c *IntIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
   353  func (s *IntIntervalConstraint) Operands() []ssa.Value     { return nil }
   354  
   355  func (c *IntArithmeticConstraint) String() string {
   356  	return fmt.Sprintf("%s = %s %s %s", c.Y().Name(), c.A.Name(), c.Op, c.B.Name())
   357  }
   358  func (c *IntConversionConstraint) String() string {
   359  	return fmt.Sprintf("%s = %s(%s)", c.Y().Name(), c.Y().Type(), c.X.Name())
   360  }
   361  func (c *IntIntersectionConstraint) String() string {
   362  	return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
   363  }
   364  func (c *IntIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
   365  
   366  func (c *IntArithmeticConstraint) Eval(g *Graph) Range {
   367  	i1, i2 := g.Range(c.A).(IntInterval), g.Range(c.B).(IntInterval)
   368  	if !i1.IsKnown() || !i2.IsKnown() {
   369  		return IntInterval{}
   370  	}
   371  	return c.Fn(i1, i2)
   372  }
   373  func (c *IntConversionConstraint) Eval(g *Graph) Range {
   374  	s := &types.StdSizes{
   375  		// XXX is it okay to assume the largest word size, or do we
   376  		// need to be platform specific?
   377  		WordSize: 8,
   378  		MaxAlign: 1,
   379  	}
   380  	fromI := g.Range(c.X).(IntInterval)
   381  	toI := g.Range(c.Y()).(IntInterval)
   382  	fromT := c.X.Type().Underlying().(*types.Basic)
   383  	toT := c.Y().Type().Underlying().(*types.Basic)
   384  	fromB := s.Sizeof(c.X.Type())
   385  	toB := s.Sizeof(c.Y().Type())
   386  
   387  	if !fromI.IsKnown() {
   388  		return toI
   389  	}
   390  	if !toI.IsKnown() {
   391  		return fromI
   392  	}
   393  
   394  	// uint<N> -> sint/uint<M>, M > N: [max(0, l1), min(2**N-1, u2)]
   395  	if (fromT.Info()&types.IsUnsigned != 0) &&
   396  		toB > fromB {
   397  
   398  		n := big.NewInt(1)
   399  		n.Lsh(n, uint(fromB*8))
   400  		n.Sub(n, big.NewInt(1))
   401  		return NewIntInterval(
   402  			MaxZ(NewZ(0), fromI.Lower),
   403  			MinZ(NewBigZ(n), toI.Upper),
   404  		)
   405  	}
   406  
   407  	// sint<N> -> sint<M>, M > N; [max(-∞, l1), min(2**N-1, u2)]
   408  	if (fromT.Info()&types.IsUnsigned == 0) &&
   409  		(toT.Info()&types.IsUnsigned == 0) &&
   410  		toB > fromB {
   411  
   412  		n := big.NewInt(1)
   413  		n.Lsh(n, uint(fromB*8))
   414  		n.Sub(n, big.NewInt(1))
   415  		return NewIntInterval(
   416  			MaxZ(NInfinity, fromI.Lower),
   417  			MinZ(NewBigZ(n), toI.Upper),
   418  		)
   419  	}
   420  
   421  	return fromI
   422  }
   423  func (c *IntIntersectionConstraint) Eval(g *Graph) Range {
   424  	xi := g.Range(c.A).(IntInterval)
   425  	if !xi.IsKnown() {
   426  		return c.I
   427  	}
   428  	return xi.Intersection(c.I)
   429  }
   430  func (c *IntIntervalConstraint) Eval(*Graph) Range { return c.I }
   431  
   432  func (c *IntIntersectionConstraint) Futures() []ssa.Value {
   433  	return []ssa.Value{c.B}
   434  }
   435  
   436  func (c *IntIntersectionConstraint) Resolve() {
   437  	r, ok := c.ranges[c.B].(IntInterval)
   438  	if !ok {
   439  		c.I = InfinityFor(c.Y())
   440  		return
   441  	}
   442  
   443  	switch c.Op {
   444  	case token.EQL:
   445  		c.I = r
   446  	case token.GTR:
   447  		c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
   448  	case token.GEQ:
   449  		c.I = NewIntInterval(r.Lower, PInfinity)
   450  	case token.LSS:
   451  		// TODO(dh): do we need 0 instead of NInfinity for uints?
   452  		c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
   453  	case token.LEQ:
   454  		c.I = NewIntInterval(NInfinity, r.Upper)
   455  	case token.NEQ:
   456  		c.I = InfinityFor(c.Y())
   457  	default:
   458  		panic("unsupported op " + c.Op.String())
   459  	}
   460  }
   461  
   462  func (c *IntIntersectionConstraint) IsKnown() bool {
   463  	return c.I.IsKnown()
   464  }
   465  
   466  func (c *IntIntersectionConstraint) MarkUnresolved() {
   467  	c.resolved = false
   468  }
   469  
   470  func (c *IntIntersectionConstraint) MarkResolved() {
   471  	c.resolved = true
   472  }
   473  
   474  func (c *IntIntersectionConstraint) IsResolved() bool {
   475  	return c.resolved
   476  }