github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/gosmith/expr.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  )
     7  
     8  func (smith *Smith) initExpressions() {
     9  	smith.expressions = []func(res *Type) string{
    10  		smith.exprLiteral,
    11  		smith.exprVar,
    12  		smith.exprFunc,
    13  		smith.exprSelectorField,
    14  		smith.exprRecv,
    15  		smith.exprArith,
    16  		smith.exprEqual,
    17  		smith.exprOrder,
    18  		smith.exprCall,
    19  		smith.exprCallBuiltin,
    20  		smith.exprAddress,
    21  		smith.exprDeref,
    22  		smith.exprSlice,
    23  		smith.exprIndexSlice,
    24  		smith.exprIndexArray,
    25  		smith.exprIndexString,
    26  		smith.exprIndexMap,
    27  		smith.exprConversion,
    28  	}
    29  }
    30  
    31  func (smith *Smith) expression(res *Type) string {
    32  	smith.exprCount++
    33  	smith.totalExprCount++
    34  	if smith.exprDepth >= NExprDepth || smith.exprCount >= NExprCount || smith.totalExprCount >= NTotalExprCount {
    35  		return res.literal()
    36  	}
    37  	for {
    38  		smith.exprDepth++
    39  		s := smith.expressions[smith.rnd(len(smith.expressions))](res)
    40  		smith.exprDepth--
    41  		if s != "" {
    42  			return s
    43  		}
    44  	}
    45  }
    46  
    47  func (smith *Smith) rvalue(t *Type) string {
    48  	return smith.expression(t)
    49  }
    50  
    51  // rvalue, but not a const
    52  // used to index arrays and strings
    53  func (smith *Smith) nonconstRvalue(t *Type) string {
    54  	if t.class != ClassNumeric {
    55  		panic("bad")
    56  	}
    57  trying:
    58  	for {
    59  		res := ""
    60  		switch smith.choice("lvalue", "call", "len", "selector", "recv", "arith", "indexMap", "conv") {
    61  		case "lvalue":
    62  			res = smith.lvalue(t)
    63  		case "call":
    64  			res = smith.exprCall(t)
    65  		case "len":
    66  			tt := smith.atype(TraitLenCapable)
    67  			fn := smith.choice("len", "cap")
    68  			if (tt.class == ClassString || tt.class == ClassMap) && fn == "cap" {
    69  				break
    70  			}
    71  			if tt.class == ClassArray {
    72  				// len/cap are const
    73  				break
    74  			}
    75  			res = F("(%v)((%v)(%v))", t.id, fn, smith.lvalue(tt))
    76  		case "selector":
    77  			res = smith.exprSelectorField(t)
    78  		case "recv":
    79  			res = smith.exprRecv(t)
    80  		case "arith":
    81  			res = F("(%v) %v (%v)", smith.lvalue(t), smith.choice("+", "-"), smith.rvalue(t))
    82  		case "indexMap":
    83  			res = smith.exprIndexMap(t)
    84  		case "conv":
    85  			res = F("(%v)(%v %v)", t.id, smith.lvalue(smith.atype(ClassNumeric)), smith.choice("", ","))
    86  		default:
    87  			panic("bad")
    88  		}
    89  		if res == "" {
    90  			continue trying
    91  		}
    92  		return res
    93  	}
    94  }
    95  
    96  func (smith *Smith) lvalue(t *Type) string {
    97  	for {
    98  		switch smith.choice("var", "indexSlice", "indexArray", "selector", "deref") {
    99  		case "var":
   100  			return smith.exprVar(t)
   101  		case "indexSlice":
   102  			return smith.exprIndexSlice(t)
   103  		case "indexArray":
   104  			return F("(%v)[%v]", smith.lvalue(smith.arrayOf(t)), smith.nonconstRvalue(smith.intType))
   105  		case "selector":
   106  			for i := 0; i < 10; i++ {
   107  				st := smith.atype(ClassStruct)
   108  				for _, e := range st.elems {
   109  					if e.typ == t {
   110  						return F("(%v).%v", smith.lvalue(st), e.id)
   111  					}
   112  				}
   113  			}
   114  			continue
   115  		case "deref":
   116  			return smith.exprDeref(t)
   117  		default:
   118  			panic("bad")
   119  		}
   120  	}
   121  }
   122  
   123  func (smith *Smith) lvalueOrBlank(t *Type) string {
   124  	for {
   125  		switch smith.choice("lvalue", "map", "blank") {
   126  		case "lvalue":
   127  			return smith.lvalue(t)
   128  		case "map":
   129  			if e := smith.exprIndexMap(t); e != "" {
   130  				return e
   131  			}
   132  		case "blank":
   133  			return "_"
   134  		default:
   135  			panic("bad")
   136  		}
   137  	}
   138  }
   139  
   140  func (smith *Smith) lvalueOrMapIndex(t *Type) string {
   141  	for {
   142  		switch smith.choice("lvalue", "map") {
   143  		case "lvalue":
   144  			return smith.lvalue(t)
   145  		case "map":
   146  			if e := smith.exprIndexMap(t); e != "" {
   147  				return e
   148  			}
   149  		default:
   150  			panic("bad")
   151  		}
   152  	}
   153  }
   154  
   155  func (smith *Smith) fmtRvalueList(list []*Type) string {
   156  	var buf bytes.Buffer
   157  	for i, t := range list {
   158  		if i != 0 {
   159  			buf.Write([]byte{','})
   160  		}
   161  		fmt.Fprintf(&buf, "%v", smith.rvalue(t))
   162  	}
   163  	return buf.String()
   164  }
   165  
   166  func (smith *Smith) fmtLvalueList(list []*Type) string {
   167  	var buf bytes.Buffer
   168  	for i, t := range list {
   169  		if i != 0 {
   170  			buf.Write([]byte{','})
   171  		}
   172  		buf.WriteString(smith.lvalueOrBlank(t))
   173  	}
   174  	return buf.String()
   175  }
   176  
   177  func (smith *Smith) fmtOasVarList(list []*Type) (str string, newVars []*Var) {
   178  	allVars := smith.vars()
   179  	var buf bytes.Buffer
   180  	for i, t := range list {
   181  		expr := "_"
   182  		// First, try to find an existing var in the same scope.
   183  		if smith.rndBool() {
   184  			for i, v := range allVars {
   185  				if v.typ == t && v.block == smith.curBlock {
   186  					allVars[i] = allVars[len(allVars)-1]
   187  					allVars = allVars[:len(allVars)-1]
   188  					expr = v.id
   189  					break
   190  				}
   191  			}
   192  		}
   193  		if smith.rndBool() || (i == len(list)-1 && len(newVars) == 0) {
   194  			expr = smith.newId("Var")
   195  			newVars = append(newVars, &Var{id: expr, typ: t})
   196  		}
   197  
   198  		if i != 0 {
   199  			buf.WriteString(", ")
   200  		}
   201  		buf.WriteString(expr)
   202  	}
   203  	return buf.String(), newVars
   204  }
   205  
   206  func (smith *Smith) exprLiteral(res *Type) string {
   207  	if res.complexLiteral != nil {
   208  		return res.complexLiteral()
   209  	}
   210  	return res.literal()
   211  }
   212  
   213  func (smith *Smith) exprVar(res *Type) string {
   214  	for _, v := range smith.vars() {
   215  		if v.typ == res {
   216  			return v.id
   217  		}
   218  	}
   219  	return smith.materializeVar(res)
   220  }
   221  
   222  func (smith *Smith) exprSelectorField(res *Type) string {
   223  	for i := 0; i < 10; i++ {
   224  		st := smith.atype(ClassStruct)
   225  		for _, e := range st.elems {
   226  			if e.typ == res {
   227  				return F("(%v).%v", smith.rvalue(st), e.id)
   228  			}
   229  		}
   230  	}
   231  	return ""
   232  }
   233  
   234  func (smith *Smith) exprFunc(res *Type) string {
   235  	if !smith.satisfiesTrait(res, TraitGlobal) {
   236  		return ""
   237  	}
   238  	var f *Func
   239  	for _, f1 := range smith.packages[smith.curPackage].toplevFuncs {
   240  		if len(f1.rets) == 1 && f1.rets[0] == res {
   241  			f = f1
   242  			break
   243  		}
   244  	}
   245  	if f == nil {
   246  		f = smith.materializeFunc([]*Type{res})
   247  	}
   248  	if smith.rndBool() {
   249  		return F("%v(%v)", f.name, smith.fmtRvalueList(f.args))
   250  	} else {
   251  		var f0 *Func
   252  	loop:
   253  		for _, f1 := range smith.packages[smith.curPackage].toplevFuncs {
   254  			if len(f1.rets) == len(f.args) {
   255  				for i := range f.args {
   256  					// TODO: check assignability
   257  					if f1.rets[i] != f.args[i] {
   258  						continue loop
   259  					}
   260  				}
   261  				f0 = f1
   262  				break
   263  			}
   264  		}
   265  		if f0 == nil {
   266  			f0 = smith.materializeFunc(f.args)
   267  		}
   268  		return F("%v(%v(%v))", f.name, f0.name, smith.fmtRvalueList(f0.args))
   269  	}
   270  }
   271  
   272  func (smith *Smith) exprAddress(res *Type) string {
   273  	if res.class != ClassPointer {
   274  		return ""
   275  	}
   276  	if res.ktyp.class == ClassStruct && smith.rndBool() {
   277  		return F("&%v", res.ktyp.complexLiteral())
   278  	}
   279  	return F("(%v)(&(%v))", res.id, smith.lvalue(res.ktyp))
   280  }
   281  
   282  func (smith *Smith) exprDeref(res *Type) string {
   283  	return F("(*(%v))", smith.lvalue(pointerTo(res)))
   284  }
   285  
   286  func (smith *Smith) exprRecv(res *Type) string {
   287  	t := smith.chanOf(res)
   288  	return F("(<- %v)", smith.rvalue(t))
   289  }
   290  
   291  func (smith *Smith) exprArith(res *Type) string {
   292  	if res.class != ClassNumeric && res.class != ClassComplex {
   293  		return ""
   294  	}
   295  	// "/" causes division by zero
   296  	// "*" causes generation of -1 index in int(real(1i * 1i))
   297  	return F("(%v) + (%v)", smith.rvalue(res), smith.rvalue(res))
   298  }
   299  
   300  func (smith *Smith) exprEqual(res *Type) string {
   301  	if res != smith.boolType {
   302  		return ""
   303  	}
   304  	t := smith.atype(TraitComparable)
   305  	return F("(%v) %v (%v)", smith.rvalue(t), smith.choice("==", "!="), smith.rvalue(t))
   306  }
   307  
   308  func (smith *Smith) exprOrder(res *Type) string {
   309  	if res != smith.boolType {
   310  		return ""
   311  	}
   312  	t := smith.atype(TraitOrdered)
   313  	return F("(%v) %v (%v)", smith.rvalue(t), smith.choice("<", "<=", ">", ">="), smith.rvalue(t))
   314  
   315  }
   316  
   317  func (smith *Smith) exprCall(ret *Type) string {
   318  	t := smith.funcOf(smith.atypeList(TraitAny), []*Type{ret})
   319  	return F("%v(%v)", smith.rvalue(t), smith.fmtRvalueList(t.styp))
   320  }
   321  
   322  func (smith *Smith) exprCallBuiltin(ret *Type) string {
   323  	switch fn := smith.choice("append", "cap", "complex", "copy", "imag", "len", "make", "new", "real", "recover"); fn {
   324  	case "append":
   325  		if ret.class != ClassSlice {
   326  			return ""
   327  		}
   328  		switch smith.choice("one", "two", "slice") {
   329  		case "one":
   330  			return F("%v(%v, %v)", fn, smith.rvalue(ret), smith.rvalue(ret.ktyp))
   331  		case "two":
   332  			return F("%v(%v, %v, %v)", fn, smith.rvalue(ret), smith.rvalue(ret.ktyp), smith.rvalue(ret.ktyp))
   333  		case "slice":
   334  			return F("%v(%v, %v...)", fn, smith.rvalue(ret), smith.rvalue(ret))
   335  		default:
   336  			panic("bad")
   337  		}
   338  	case "len", "cap":
   339  		if ret != smith.intType { // TODO: must be convertable
   340  			return ""
   341  		}
   342  		t := smith.atype(TraitLenCapable)
   343  		if (t.class == ClassString || t.class == ClassMap) && fn == "cap" {
   344  			return ""
   345  
   346  		}
   347  		return F("%v(%v)", fn, smith.rvalue(t))
   348  	case "copy":
   349  		if ret != smith.intType {
   350  			return ""
   351  		}
   352  		return F("%v", smith.exprCopySlice())
   353  	case "make":
   354  		if ret.class != ClassSlice && ret.class != ClassMap && ret.class != ClassChan {
   355  			return ""
   356  		}
   357  		cap := ""
   358  		if ret.class == ClassSlice {
   359  			if smith.rndBool() {
   360  				cap = F(", %v", smith.rvalue(smith.intType))
   361  			} else {
   362  				// Careful to not generate "len larger than cap".
   363  				cap = F(", 0, %v", smith.rvalue(smith.intType))
   364  			}
   365  		} else if smith.rndBool() {
   366  			cap = F(", %v", smith.rvalue(smith.intType))
   367  		}
   368  		return F("make(%v %v)", ret.id, cap)
   369  	case "new":
   370  		if ret.class != ClassPointer {
   371  			return ""
   372  		}
   373  		return F("new(%v)", ret.ktyp.id)
   374  	case "recover":
   375  		if ret != smith.efaceType {
   376  			return ""
   377  		}
   378  		return "recover()"
   379  	case "real", "imag":
   380  		if ret == smith.float32Type {
   381  			return F("real(%v)", smith.rvalue(smith.complex64Type))
   382  		}
   383  		if ret == smith.float64Type {
   384  			return F("real(%v)", smith.rvalue(smith.complex128Type))
   385  		}
   386  		return ""
   387  	case "complex":
   388  		if ret == smith.complex64Type {
   389  			return F("complex(%v, %v)", smith.rvalue(smith.float32Type), smith.rvalue(smith.float32Type))
   390  		}
   391  		if ret == smith.complex128Type {
   392  			return F("complex(%v, %v)", smith.rvalue(smith.float64Type), smith.rvalue(smith.float64Type))
   393  		}
   394  		return ""
   395  	default:
   396  		panic("bad")
   397  	}
   398  }
   399  
   400  func (smith *Smith) exprCopySlice() string {
   401  	if smith.rndBool() {
   402  		t := smith.atype(ClassSlice)
   403  		return F("copy(%v, %v)", smith.rvalue(t), smith.rvalue(t))
   404  	} else {
   405  		return F("copy(%v, %v)", smith.rvalue(smith.sliceOf(smith.byteType)), smith.rvalue(smith.stringType))
   406  	}
   407  }
   408  
   409  func (smith *Smith) exprSlice(ret *Type) string {
   410  	if ret.class != ClassSlice {
   411  		return ""
   412  	}
   413  	i0 := ""
   414  	if smith.rndBool() {
   415  		i0 = smith.nonconstRvalue(smith.intType)
   416  	}
   417  	i2 := ""
   418  	if smith.rndBool() {
   419  		i2 = ":" + smith.nonconstRvalue(smith.intType)
   420  	}
   421  	i1 := ":"
   422  	if smith.rndBool() || i2 != "" {
   423  		i1 = ":" + smith.nonconstRvalue(smith.intType)
   424  	}
   425  	return F("(%v)[%v%v%v]", smith.rvalue(ret), i0, i1, i2)
   426  }
   427  
   428  func (smith *Smith) exprIndexSlice(ret *Type) string {
   429  	return F("(%v)[%v]", smith.rvalue(smith.sliceOf(ret)), smith.nonconstRvalue(smith.intType))
   430  }
   431  
   432  func (smith *Smith) exprIndexString(ret *Type) string {
   433  	if ret != smith.byteType {
   434  		return ""
   435  	}
   436  	return F("(%v)[%v]", smith.rvalue(smith.stringType), smith.nonconstRvalue(smith.intType))
   437  }
   438  
   439  func (smith *Smith) exprIndexArray(ret *Type) string {
   440  	// TODO: also handle indexing of pointers to arrays
   441  	return F("(%v)[%v]", smith.rvalue(smith.arrayOf(ret)), smith.nonconstRvalue(smith.intType))
   442  }
   443  
   444  func (smith *Smith) exprIndexMap(ret *Type) string {
   445  	// TODO: figure out something better
   446  	for i := 0; i < 10; i++ {
   447  		t := smith.atype(ClassMap)
   448  		if t.vtyp == ret {
   449  			return F("(%v)[%v]", smith.rvalue(t), smith.rvalue(t.ktyp))
   450  		}
   451  	}
   452  	return ""
   453  }
   454  
   455  func (smith *Smith) exprConversion(ret *Type) string {
   456  	if ret.class == ClassNumeric {
   457  		return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.atype(ClassNumeric)), smith.choice("", ","))
   458  	}
   459  	if ret.class == ClassComplex {
   460  		return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.atype(ClassComplex)), smith.choice("", ","))
   461  	}
   462  	if ret == smith.stringType {
   463  		switch smith.choice("int", "byteSlice", "runeSlice") {
   464  		case "int":
   465  			// We produce a string of length at least 3, to not produce
   466  			// "invalid string index 1 (out of bounds for 1-byte string)"
   467  			return F("(%v)((%v) + (1<<24) %v)", ret.id, smith.rvalue(smith.intType), smith.choice("", ","))
   468  		case "byteSlice":
   469  			return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.sliceOf(smith.byteType)), smith.choice("", ","))
   470  		case "runeSlice":
   471  			return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.sliceOf(smith.runeType)), smith.choice("", ","))
   472  		default:
   473  			panic("bad")
   474  		}
   475  	}
   476  	if ret.class == ClassSlice && (ret.ktyp == smith.byteType || ret.ktyp == smith.runeType) {
   477  		return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.stringType), smith.choice("", ","))
   478  	}
   479  	// TODO: handle "x is assignable to T"
   480  	// TODO: handle "x's type and T have identical underlying types"
   481  	// TODO: handle "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
   482  	return ""
   483  }