 6a979fb40d
			
		
	
	
		6a979fb40d
		
			
		
	
	
	
	
		
			
			* update staticcheck Don't fork staticcheck: use the upstream version. Remove unneeded SSA loading. * Cache go/analysis facts Don't load unneeded packages for go/analysis. Repeated run of go/analysis linters now 10x faster (2s vs 20s on this repo) than before.
		
			
				
	
	
		
			274 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package vrp
 | |
| 
 | |
| // TODO(dh): most of the constraints have implementations identical to
 | |
| // that of strings. Consider reusing them.
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/types"
 | |
| 
 | |
| 	"honnef.co/go/tools/ssa"
 | |
| )
 | |
| 
 | |
| type SliceInterval struct {
 | |
| 	Length IntInterval
 | |
| }
 | |
| 
 | |
| func (s SliceInterval) Union(other Range) Range {
 | |
| 	i, ok := other.(SliceInterval)
 | |
| 	if !ok {
 | |
| 		i = SliceInterval{EmptyIntInterval}
 | |
| 	}
 | |
| 	if s.Length.Empty() || !s.Length.IsKnown() {
 | |
| 		return i
 | |
| 	}
 | |
| 	if i.Length.Empty() || !i.Length.IsKnown() {
 | |
| 		return s
 | |
| 	}
 | |
| 	return SliceInterval{
 | |
| 		Length: s.Length.Union(i.Length).(IntInterval),
 | |
| 	}
 | |
| }
 | |
| func (s SliceInterval) String() string { return s.Length.String() }
 | |
| func (s SliceInterval) IsKnown() bool  { return s.Length.IsKnown() }
 | |
| 
 | |
| type SliceAppendConstraint struct {
 | |
| 	aConstraint
 | |
| 	A ssa.Value
 | |
| 	B ssa.Value
 | |
| }
 | |
| 
 | |
| type SliceSliceConstraint struct {
 | |
| 	aConstraint
 | |
| 	X     ssa.Value
 | |
| 	Lower ssa.Value
 | |
| 	Upper ssa.Value
 | |
| }
 | |
| 
 | |
| type ArraySliceConstraint struct {
 | |
| 	aConstraint
 | |
| 	X     ssa.Value
 | |
| 	Lower ssa.Value
 | |
| 	Upper ssa.Value
 | |
| }
 | |
| 
 | |
| type SliceIntersectionConstraint struct {
 | |
| 	aConstraint
 | |
| 	X ssa.Value
 | |
| 	I IntInterval
 | |
| }
 | |
| 
 | |
| type SliceLengthConstraint struct {
 | |
| 	aConstraint
 | |
| 	X ssa.Value
 | |
| }
 | |
| 
 | |
| type MakeSliceConstraint struct {
 | |
| 	aConstraint
 | |
| 	Size ssa.Value
 | |
| }
 | |
| 
 | |
| type SliceIntervalConstraint struct {
 | |
| 	aConstraint
 | |
| 	I IntInterval
 | |
| }
 | |
| 
 | |
| func NewSliceAppendConstraint(a, b, y ssa.Value) Constraint {
 | |
| 	return &SliceAppendConstraint{NewConstraint(y), a, b}
 | |
| }
 | |
| func NewSliceSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
 | |
| 	return &SliceSliceConstraint{NewConstraint(y), x, lower, upper}
 | |
| }
 | |
| func NewArraySliceConstraint(x, lower, upper, y ssa.Value) Constraint {
 | |
| 	return &ArraySliceConstraint{NewConstraint(y), x, lower, upper}
 | |
| }
 | |
| func NewSliceIntersectionConstraint(x ssa.Value, i IntInterval, y ssa.Value) Constraint {
 | |
| 	return &SliceIntersectionConstraint{NewConstraint(y), x, i}
 | |
| }
 | |
| func NewSliceLengthConstraint(x, y ssa.Value) Constraint {
 | |
| 	return &SliceLengthConstraint{NewConstraint(y), x}
 | |
| }
 | |
| func NewMakeSliceConstraint(size, y ssa.Value) Constraint {
 | |
| 	return &MakeSliceConstraint{NewConstraint(y), size}
 | |
| }
 | |
| func NewSliceIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
 | |
| 	return &SliceIntervalConstraint{NewConstraint(y), i}
 | |
| }
 | |
| 
 | |
| func (c *SliceAppendConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
 | |
| func (c *SliceSliceConstraint) Operands() []ssa.Value {
 | |
| 	ops := []ssa.Value{c.X}
 | |
| 	if c.Lower != nil {
 | |
| 		ops = append(ops, c.Lower)
 | |
| 	}
 | |
| 	if c.Upper != nil {
 | |
| 		ops = append(ops, c.Upper)
 | |
| 	}
 | |
| 	return ops
 | |
| }
 | |
| func (c *ArraySliceConstraint) Operands() []ssa.Value {
 | |
| 	ops := []ssa.Value{c.X}
 | |
| 	if c.Lower != nil {
 | |
| 		ops = append(ops, c.Lower)
 | |
| 	}
 | |
| 	if c.Upper != nil {
 | |
| 		ops = append(ops, c.Upper)
 | |
| 	}
 | |
| 	return ops
 | |
| }
 | |
| func (c *SliceIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
 | |
| func (c *SliceLengthConstraint) Operands() []ssa.Value       { return []ssa.Value{c.X} }
 | |
| func (c *MakeSliceConstraint) Operands() []ssa.Value         { return []ssa.Value{c.Size} }
 | |
| func (s *SliceIntervalConstraint) Operands() []ssa.Value     { return nil }
 | |
| 
 | |
| func (c *SliceAppendConstraint) String() string {
 | |
| 	return fmt.Sprintf("%s = append(%s, %s)", c.Y().Name(), c.A.Name(), c.B.Name())
 | |
| }
 | |
| func (c *SliceSliceConstraint) String() string {
 | |
| 	var lname, uname string
 | |
| 	if c.Lower != nil {
 | |
| 		lname = c.Lower.Name()
 | |
| 	}
 | |
| 	if c.Upper != nil {
 | |
| 		uname = c.Upper.Name()
 | |
| 	}
 | |
| 	return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
 | |
| }
 | |
| func (c *ArraySliceConstraint) String() string {
 | |
| 	var lname, uname string
 | |
| 	if c.Lower != nil {
 | |
| 		lname = c.Lower.Name()
 | |
| 	}
 | |
| 	if c.Upper != nil {
 | |
| 		uname = c.Upper.Name()
 | |
| 	}
 | |
| 	return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
 | |
| }
 | |
| func (c *SliceIntersectionConstraint) String() string {
 | |
| 	return fmt.Sprintf("%s = %s.%t ⊓ %s", c.Y().Name(), c.X.Name(), c.Y().(*ssa.Sigma).Branch, c.I)
 | |
| }
 | |
| func (c *SliceLengthConstraint) String() string {
 | |
| 	return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
 | |
| }
 | |
| func (c *MakeSliceConstraint) String() string {
 | |
| 	return fmt.Sprintf("%s = make(slice, %s)", c.Y().Name(), c.Size.Name())
 | |
| }
 | |
| func (c *SliceIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
 | |
| 
 | |
| func (c *SliceAppendConstraint) Eval(g *Graph) Range {
 | |
| 	l1 := g.Range(c.A).(SliceInterval).Length
 | |
| 	var l2 IntInterval
 | |
| 	switch r := g.Range(c.B).(type) {
 | |
| 	case SliceInterval:
 | |
| 		l2 = r.Length
 | |
| 	case StringInterval:
 | |
| 		l2 = r.Length
 | |
| 	default:
 | |
| 		return SliceInterval{}
 | |
| 	}
 | |
| 	if !l1.IsKnown() || !l2.IsKnown() {
 | |
| 		return SliceInterval{}
 | |
| 	}
 | |
| 	return SliceInterval{
 | |
| 		Length: l1.Add(l2),
 | |
| 	}
 | |
| }
 | |
| func (c *SliceSliceConstraint) Eval(g *Graph) Range {
 | |
| 	lr := NewIntInterval(NewZ(0), NewZ(0))
 | |
| 	if c.Lower != nil {
 | |
| 		lr = g.Range(c.Lower).(IntInterval)
 | |
| 	}
 | |
| 	ur := g.Range(c.X).(SliceInterval).Length
 | |
| 	if c.Upper != nil {
 | |
| 		ur = g.Range(c.Upper).(IntInterval)
 | |
| 	}
 | |
| 	if !lr.IsKnown() || !ur.IsKnown() {
 | |
| 		return SliceInterval{}
 | |
| 	}
 | |
| 
 | |
| 	ls := []Z{
 | |
| 		ur.Lower.Sub(lr.Lower),
 | |
| 		ur.Upper.Sub(lr.Lower),
 | |
| 		ur.Lower.Sub(lr.Upper),
 | |
| 		ur.Upper.Sub(lr.Upper),
 | |
| 	}
 | |
| 	// TODO(dh): if we don't truncate lengths to 0 we might be able to
 | |
| 	// easily detect slices with high < low. we'd need to treat -∞
 | |
| 	// specially, though.
 | |
| 	for i, l := range ls {
 | |
| 		if l.Sign() == -1 {
 | |
| 			ls[i] = NewZ(0)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return SliceInterval{
 | |
| 		Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
 | |
| 	}
 | |
| }
 | |
| func (c *ArraySliceConstraint) Eval(g *Graph) Range {
 | |
| 	lr := NewIntInterval(NewZ(0), NewZ(0))
 | |
| 	if c.Lower != nil {
 | |
| 		lr = g.Range(c.Lower).(IntInterval)
 | |
| 	}
 | |
| 	var l int64
 | |
| 	switch typ := c.X.Type().(type) {
 | |
| 	case *types.Array:
 | |
| 		l = typ.Len()
 | |
| 	case *types.Pointer:
 | |
| 		l = typ.Elem().(*types.Array).Len()
 | |
| 	}
 | |
| 	ur := NewIntInterval(NewZ(l), NewZ(l))
 | |
| 	if c.Upper != nil {
 | |
| 		ur = g.Range(c.Upper).(IntInterval)
 | |
| 	}
 | |
| 	if !lr.IsKnown() || !ur.IsKnown() {
 | |
| 		return SliceInterval{}
 | |
| 	}
 | |
| 
 | |
| 	ls := []Z{
 | |
| 		ur.Lower.Sub(lr.Lower),
 | |
| 		ur.Upper.Sub(lr.Lower),
 | |
| 		ur.Lower.Sub(lr.Upper),
 | |
| 		ur.Upper.Sub(lr.Upper),
 | |
| 	}
 | |
| 	// TODO(dh): if we don't truncate lengths to 0 we might be able to
 | |
| 	// easily detect slices with high < low. we'd need to treat -∞
 | |
| 	// specially, though.
 | |
| 	for i, l := range ls {
 | |
| 		if l.Sign() == -1 {
 | |
| 			ls[i] = NewZ(0)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return SliceInterval{
 | |
| 		Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
 | |
| 	}
 | |
| }
 | |
| func (c *SliceIntersectionConstraint) Eval(g *Graph) Range {
 | |
| 	xi := g.Range(c.X).(SliceInterval)
 | |
| 	if !xi.IsKnown() {
 | |
| 		return c.I
 | |
| 	}
 | |
| 	return SliceInterval{
 | |
| 		Length: xi.Length.Intersection(c.I),
 | |
| 	}
 | |
| }
 | |
| func (c *SliceLengthConstraint) Eval(g *Graph) Range {
 | |
| 	i := g.Range(c.X).(SliceInterval).Length
 | |
| 	if !i.IsKnown() {
 | |
| 		return NewIntInterval(NewZ(0), PInfinity)
 | |
| 	}
 | |
| 	return i
 | |
| }
 | |
| func (c *MakeSliceConstraint) Eval(g *Graph) Range {
 | |
| 	i, ok := g.Range(c.Size).(IntInterval)
 | |
| 	if !ok {
 | |
| 		return SliceInterval{NewIntInterval(NewZ(0), PInfinity)}
 | |
| 	}
 | |
| 	if i.Lower.Sign() == -1 {
 | |
| 		i.Lower = NewZ(0)
 | |
| 	}
 | |
| 	return SliceInterval{i}
 | |
| }
 | |
| func (c *SliceIntervalConstraint) Eval(*Graph) Range { return SliceInterval{c.I} }
 |