├── .gitignore ├── go.mod ├── utils └── print_ast.go ├── microsmith ├── common.go ├── program_test.go ├── scope.go ├── context.go ├── program.go ├── package.go ├── stmt.go ├── expr.go └── types.go ├── main.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | work/ 2 | ms 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ALTree/microsmith 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /utils/print_ast.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/ast" 5 | "go/parser" 6 | "go/token" 7 | ) 8 | 9 | var src string = ` 10 | package p 11 | 12 | func f() { 13 | for i := range 10 { 14 | println(i) 15 | } 16 | } 17 | ` 18 | 19 | func main() { 20 | fset := token.NewFileSet() 21 | f, err := parser.ParseFile(fset, "", src, 0) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | ast.Print(fset, f) 27 | } 28 | -------------------------------------------------------------------------------- /microsmith/common.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // Returns a random ASCII string 10 | func RandString() string { 11 | const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 12 | 13 | n := int(rand.NormFloat64()*8.0 + 12.0) 14 | if n < 0 { 15 | n = 0 16 | } 17 | 18 | sb := strings.Builder{} 19 | sb.Grow(n + 2) 20 | sb.WriteByte('"') 21 | for i := 0; i < n; i++ { 22 | sb.WriteByte(chars[rand.Int63()%int64(len(chars))]) 23 | } 24 | sb.WriteByte('"') 25 | return sb.String() 26 | } 27 | 28 | // returns a random rune literal 29 | func RandRune() string { 30 | switch rand.Intn(3) { 31 | case 0: 32 | // single character within the quotes: 'a' 33 | return "'" + string(byte('0'+rand.Intn('Z'-'0'))) + "'" 34 | case 1: 35 | // \x followed by exactly two hexadecimal digits: \x4f 36 | return "'\\x" + strconv.FormatInt(0x10+int64(rand.Intn(0xff-0x10)), 16) + "'" 37 | case 2: 38 | // \u followed by exactly four hexadecimal digits: \u3b7f 39 | return "'\\u" + strconv.FormatInt(0x1000+int64(rand.Intn(0xd000-0x1000)), 16) + "'" 40 | default: 41 | panic("unreachable") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /microsmith/program_test.go: -------------------------------------------------------------------------------- 1 | package microsmith_test 2 | 3 | import ( 4 | "go/ast" 5 | "io/ioutil" 6 | "math/rand" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/ALTree/microsmith/microsmith" 13 | ) 14 | 15 | const WorkDir = "work" 16 | 17 | // check n generated programs with go/types 18 | func testProgramGoTypes(t *testing.T, n int, conf microsmith.ProgramConf) { 19 | for i := 0; i < n; i++ { 20 | gp := microsmith.NewProgram(conf) 21 | err := gp.Check() 22 | if err != nil { 23 | tmpfile, _ := ioutil.TempFile("", "fail*.go") 24 | if _, err := tmpfile.Write([]byte(gp.String())); err != nil { 25 | t.Fatal(err) 26 | } 27 | t.Fatalf("Program failed typechecking:\n%s\n%v", err, gp) 28 | } 29 | } 30 | } 31 | 32 | func TestNewProgram(t *testing.T) { 33 | n := 50 34 | if testing.Short() { 35 | n = 10 36 | } 37 | testProgramGoTypes(t, n, microsmith.ProgramConf{}) 38 | } 39 | 40 | func TestNewProgramTP(t *testing.T) { 41 | n := 50 42 | if testing.Short() { 43 | n = 10 44 | } 45 | 46 | testProgramGoTypes( 47 | t, n, 48 | microsmith.ProgramConf{ 49 | MultiPkg: false, 50 | TypeParams: true, 51 | }) 52 | } 53 | 54 | func GetToolchain() string { 55 | if bin := os.Getenv("GO_TC"); bin != "" { 56 | return bin 57 | } else { 58 | return "go" 59 | } 60 | } 61 | 62 | // Check generated programs with gc (from file). 63 | func compile(t *testing.T, conf microsmith.ProgramConf) { 64 | lim := 10 65 | if testing.Short() { 66 | lim = 2 67 | } 68 | 69 | if _, err := os.Stat(WorkDir); os.IsNotExist(err) { 70 | err := os.MkdirAll(WorkDir, os.ModePerm) 71 | if err != nil { 72 | t.Fatalf("%v", err) 73 | } 74 | } 75 | 76 | // build toolchain 77 | cmd := exec.Command(GetToolchain(), "install", "std") 78 | env := append(os.Environ(), "GODEBUG=installgoroot=all") 79 | cmd.Env = env 80 | 81 | out, err := cmd.CombinedOutput() 82 | if err != nil { 83 | t.Fatalf("Installing dependencies failed with error:\n----\n%s\n%s\n----\n", out, err) 84 | } 85 | 86 | keepdir := false 87 | for i := 0; i < lim; i++ { 88 | gp := microsmith.NewProgram(conf) 89 | err := gp.WriteToDisk(WorkDir) 90 | if err != nil { 91 | t.Fatalf("Could not write to file: %s", err) 92 | } 93 | bo := microsmith.BuildOptions{ 94 | Toolchain: GetToolchain(), 95 | Noopt: false, 96 | Race: false, 97 | Ssacheck: false, 98 | Experiment: "", 99 | } 100 | out, err := gp.Compile("amd64", bo) 101 | if err != nil && !strings.Contains(out, "internal compiler error") { 102 | t.Fatalf("Generated program failed compilation:\n%s\n%s", out, err) 103 | keepdir = true 104 | } 105 | } 106 | 107 | if !keepdir { 108 | os.RemoveAll(WorkDir) 109 | } 110 | } 111 | 112 | func TestCompile(t *testing.T) { 113 | compile(t, 114 | microsmith.ProgramConf{ 115 | MultiPkg: false, 116 | TypeParams: false, 117 | }) 118 | } 119 | 120 | func TestCompileMultiPkg(t *testing.T) { 121 | compile(t, 122 | microsmith.ProgramConf{ 123 | MultiPkg: true, 124 | TypeParams: false, 125 | }) 126 | } 127 | 128 | func TestCompileTypeParams(t *testing.T) { 129 | compile(t, 130 | microsmith.ProgramConf{ 131 | MultiPkg: false, 132 | TypeParams: true, 133 | }) 134 | } 135 | 136 | func TestCompileMultiPkgTypeParams(t *testing.T) { 137 | compile(t, 138 | microsmith.ProgramConf{ 139 | MultiPkg: true, 140 | TypeParams: true, 141 | }) 142 | } 143 | 144 | var sink *ast.File 145 | 146 | func benchHelper(b *testing.B, conf microsmith.ProgramConf) { 147 | b.ReportAllocs() 148 | rand.Seed(1) 149 | prog := microsmith.NewProgramBuilder(conf) 150 | pb := microsmith.NewPackageBuilder(conf, "main", prog) 151 | b.ResetTimer() 152 | for b.Loop() { 153 | sink = pb.File() 154 | } 155 | 156 | } 157 | 158 | func BenchmarkSinglePkg(b *testing.B) { 159 | benchHelper(b, microsmith.ProgramConf{ 160 | MultiPkg: false, 161 | TypeParams: true, 162 | }) 163 | } 164 | -------------------------------------------------------------------------------- /microsmith/scope.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | ) 7 | 8 | type Variable struct { 9 | Type Type 10 | Name *ast.Ident 11 | } 12 | 13 | func (v Variable) String() string { 14 | return v.Name.String() + " " + v.Type.Name() 15 | } 16 | 17 | // A scope holds a list of all the variables that are in scope in a 18 | // given moment 19 | type Scope struct { 20 | pb *PackageBuilder 21 | vars []Variable 22 | } 23 | 24 | func (s Scope) String() string { 25 | if len(s.vars) == 0 { 26 | return "{empty scope}" 27 | } 28 | str := "{\n" 29 | for i := range s.vars { 30 | str += s.vars[i].String() + "\n" 31 | } 32 | str = str[:len(str)-1] + "\n}" 33 | return str 34 | } 35 | 36 | // Has returns true if the scope has at least one variable of Type t. 37 | func (s Scope) Has(t Type) bool { 38 | for _, v := range s.vars { 39 | if v.Type.Equal(t) { 40 | return true 41 | } 42 | } 43 | return false 44 | } 45 | 46 | func (s Scope) FindVarByName(name string) (Variable, bool) { 47 | for _, v := range s.vars { 48 | if v.Name.Name == name { 49 | return v, true 50 | } 51 | } 52 | return Variable{}, false 53 | } 54 | 55 | // NewIdent adds a new variable of Type t to the scope, automatically 56 | // assigning it a name that is not already taken. It returns a pointer 57 | // to the new variable's ast.Ident. 58 | func (s *Scope) NewIdent(t Type) *ast.Ident { 59 | tc := 0 60 | for _, v := range s.vars { 61 | if Ident(t) == Ident(v.Type) { 62 | tc++ 63 | } 64 | } 65 | name := fmt.Sprintf("%s%v", Ident(t), tc) 66 | id := &ast.Ident{Name: name} 67 | s.vars = append(s.vars, Variable{t, id}) 68 | return id 69 | } 70 | 71 | // Adds v to the scope. 72 | func (s *Scope) AddVariable(i *ast.Ident, t Type) { 73 | s.vars = append(s.vars, Variable{t, i}) 74 | } 75 | 76 | func (s *Scope) DeleteIdentByName(name *ast.Ident) { 77 | del := -1 78 | for i := range s.vars { 79 | if v := s.vars[i]; v.Name.Name == name.Name { 80 | del = i 81 | break 82 | } 83 | } 84 | 85 | if del != -1 { 86 | s.vars = append(s.vars[:del], s.vars[del+1:]...) 87 | } 88 | } 89 | 90 | // Returns a random variable in scope among the ones that satisfy 91 | // pred(v, t). If there isn't one, returns false as the second value. 92 | func (s Scope) RandPred(pred func(v Variable, t ...Type) bool, t ...Type) (Variable, bool) { 93 | vs := make([]Variable, 0, 256) 94 | for _, v := range s.vars { 95 | if pred(v, t...) { 96 | vs = append(vs, v) 97 | } 98 | } 99 | if len(vs) == 0 { 100 | return Variable{}, false 101 | } 102 | return RandItem(s.pb.rs, vs), true 103 | } 104 | 105 | // Returns a random variable in scope that can be used in the LHS of 106 | // an assignment. 107 | func (s Scope) RandAssignable() (Variable, bool) { 108 | return s.RandPred(func(v Variable, _ ...Type) bool { 109 | f, fnc := v.Type.(FuncType) 110 | return (fnc && f.Local) || !fnc 111 | }) 112 | } 113 | 114 | // Returns a random function with return type t 115 | func (s Scope) RandFuncRet(t Type) (Variable, bool) { 116 | return s.RandPred(func(v Variable, t ...Type) bool { 117 | f, fnc := v.Type.(FuncType) 118 | switch f.N { 119 | case "unsafe.SliceData": 120 | // custom handling for func with generic return type 121 | _, isPointer := t[0].(PointerType) 122 | return isPointer 123 | case "min", "max": 124 | return IsNumeric(t[0]) || t[0].Equal(BT{"string"}) 125 | } 126 | return (fnc && len(f.Ret) > 0 && f.Ret[0].Equal(t[0])) 127 | }, t) 128 | } 129 | 130 | // Returns a random function in scope; but not a predefined one. 131 | func (s Scope) RandFunc() (Variable, bool) { 132 | return s.RandPred(func(v Variable, _ ...Type) bool { 133 | f, fnc := v.Type.(FuncType) 134 | return (fnc && f.Local) // TODO(alb): why is f.Local needed here? 135 | }) 136 | } 137 | 138 | // Return a random variable of type t (exact match) 139 | func (s Scope) RandVar(t Type) (Variable, bool) { 140 | return s.RandPred(func(v Variable, t ...Type) bool { 141 | return v.Type.Equal(t[0]) 142 | }, t) 143 | } 144 | 145 | // Returns a variable containing t 146 | func (s Scope) RandVarSubType(t Type) (Variable, bool) { 147 | return s.RandPred(func(v Variable, t ...Type) bool { 148 | return v.Type.Contains(t[0]) 149 | }, t) 150 | } 151 | 152 | // Returns a random variable that can be cleared 153 | func (s Scope) RandClearable() (Variable, bool) { 154 | return s.RandPred(func(v Variable, _ ...Type) bool { 155 | switch v.Type.(type) { 156 | case ArrayType, MapType: 157 | return true 158 | default: 159 | return false 160 | } 161 | }) 162 | } 163 | 164 | // Returns a chan (of any subtype) 165 | func (s Scope) RandChan() (Variable, bool) { 166 | return s.RandPred(func(v Variable, _ ...Type) bool { 167 | _, ischan := v.Type.(ChanType) 168 | return ischan 169 | }) 170 | } 171 | 172 | // Returns a struct (of any type) 173 | func (s Scope) RandStruct() (Variable, bool) { 174 | return s.RandPred(func(v Variable, _ ...Type) bool { 175 | _, isstruct := v.Type.(StructType) 176 | return isstruct 177 | }) 178 | } 179 | 180 | func FindByName(tp []Constraint, name string) Constraint { 181 | for i := 0; i < len(tp); i++ { 182 | if tp[i].N.Name == name { 183 | return tp[i] 184 | } 185 | } 186 | panic("unreachable") 187 | } 188 | -------------------------------------------------------------------------------- /microsmith/context.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "go/ast" 5 | "math/rand" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // Context holds all the contextual information needed while 11 | // generating a random package. 12 | type Context struct { // TODO(alb): rename to PackageContext 13 | 14 | // program-wide settings for the fuzzer 15 | programConf ProgramConf 16 | 17 | // all the Costraints available declared in the package 18 | constraints []Constraint 19 | 20 | // package-wide scope of vars and func available to the code in a 21 | // given moment 22 | scope *Scope 23 | 24 | // Function-level scope of the type parameters available to the 25 | // code in the body. Reset and re-filled in when declaring a new 26 | // function. 27 | typeparams *Scope 28 | 29 | // Wheter we are building a loop body or the argument of a defer 30 | // statement. 31 | inLoop, inDefer bool 32 | } 33 | 34 | func NewContext(pc ProgramConf) *Context { 35 | return &Context{ 36 | programConf: pc, 37 | } 38 | } 39 | 40 | // ProgramConf holds program-wide configuration settings that change 41 | // the kind of programs that are generated. 42 | type ProgramConf struct { 43 | MultiPkg bool // for -multipkg 44 | TypeParams bool // for -tp 45 | } 46 | 47 | // -------------------------------- 48 | // Randomizers 49 | // -------------------------------- 50 | 51 | func RandItem[T any](r *rand.Rand, a []T) T { 52 | if r == nil { 53 | panic("RandItem: nil rand.Rand") 54 | } 55 | return a[r.Intn(len(a))] 56 | } 57 | 58 | // -------------------------------- 59 | // Types Randomizers 60 | // -------------------------------- 61 | 62 | // Returns a slice of n random types, including composite types 63 | // (structs, array, maps, chans). 64 | func (pb PackageBuilder) RandTypes(n int) []Type { 65 | types := make([]Type, n) 66 | for i := 0; i < n; i++ { 67 | types[i] = pb.RandType() 68 | } 69 | return types 70 | } 71 | 72 | // Returns a single random type (including structs, array, maps, 73 | // chans). 74 | func (pb PackageBuilder) RandType() Type { 75 | pb.typedepth++ 76 | defer func() { pb.typedepth-- }() 77 | 78 | if pb.typedepth >= 5 { 79 | return pb.RandBaseType() 80 | } 81 | 82 | switch pb.rs.Intn(15) { 83 | case 0, 1: 84 | return ArrayOf(pb.RandType()) 85 | case 2: 86 | return ChanOf(pb.RandType()) 87 | case 3, 4: 88 | return MapOf( 89 | pb.RandComparableType(), 90 | pb.RandType(), 91 | ) 92 | case 5, 6: 93 | return PointerOf(pb.RandType()) 94 | case 7, 8: 95 | return pb.RandStructType() 96 | case 9: 97 | return pb.RandFuncType() 98 | case 10: 99 | return pb.RandInterfaceType() 100 | default: 101 | return pb.RandBaseType() 102 | } 103 | } 104 | 105 | func (pb PackageBuilder) RandComparableType() Type { 106 | types := make([]Type, 0, 32) 107 | 108 | // from Base Types 109 | for _, t := range pb.baseTypes { 110 | if t.Comparable() { 111 | types = append(types, t) 112 | } 113 | } 114 | 115 | // from type parameters 116 | if tp := pb.ctx.typeparams; tp != nil { 117 | for _, v := range tp.vars { 118 | if v.Type.Comparable() { 119 | types = append(types, MakeTypeParam(v)) 120 | } 121 | } 122 | } 123 | 124 | return RandItem(pb.rs, types) 125 | } 126 | 127 | // Returns a single BaseType (primitives, or a type parameter). 128 | func (pb PackageBuilder) RandBaseType() Type { 129 | if tp := pb.ctx.typeparams; tp != nil { 130 | i := pb.rs.Intn(len(pb.baseTypes) + len(tp.vars)) 131 | if i < len(pb.baseTypes) { 132 | return pb.baseTypes[i] 133 | } else { 134 | return MakeTypeParam((tp.vars)[i-len(pb.baseTypes)]) 135 | } 136 | } else { 137 | return RandItem(pb.rs, pb.baseTypes) 138 | } 139 | } 140 | 141 | func (pb PackageBuilder) RandNumericType() BasicType { 142 | t := RandItem(pb.rs, pb.baseTypes) 143 | for !IsNumeric(t) { 144 | t = RandItem(pb.rs, pb.baseTypes) 145 | } 146 | return t.(BasicType) 147 | } 148 | 149 | func (pb PackageBuilder) RandStructType() StructType { 150 | st := StructType{[]Type{}, []string{}} 151 | for i := 0; i < pb.rs.Intn(6); i++ { 152 | t := pb.RandType() 153 | st.Ftypes = append(st.Ftypes, t) 154 | st.Fnames = append(st.Fnames, strings.Title(Ident(t))+strconv.Itoa(i)) 155 | } 156 | return st 157 | } 158 | 159 | func (pb PackageBuilder) RandFuncType() FuncType { 160 | args := make([]Type, 0, pb.rs.Intn(8)) 161 | 162 | // arguments 163 | for i := 0; i < cap(args); i++ { 164 | args = append(args, pb.RandType()) 165 | } 166 | 167 | // optionally make the last parameter variadic 168 | if len(args) > 0 && pb.rs.Intn(4) == 0 { 169 | args[len(args)-1] = EllipsisType{Base: args[len(args)-1]} 170 | } 171 | 172 | // return type 173 | ret := []Type{pb.RandType()} 174 | 175 | return FuncType{"FU", args, ret, true} 176 | } 177 | 178 | func (pb PackageBuilder) RandRangeableFuncType() FuncType { 179 | arg := FuncType{ 180 | N: "FU", 181 | Args: []Type{}, 182 | Ret: []Type{BT{"bool"}}, 183 | Local: true, 184 | } 185 | 186 | // One of 187 | // func(func()bool) bool 188 | // func(func(V)bool) bool 189 | // func(func(K, V)bool) bool 190 | for i := 0; i < pb.rs.Intn(3); i++ { 191 | arg.Args = append(arg.Args, pb.RandType()) 192 | } 193 | return FuncType{"FU", []Type{arg}, []Type{}, true} 194 | } 195 | 196 | func (pb PackageBuilder) RandInterfaceType() InterfaceType { 197 | var in InterfaceType 198 | for i := 0; i < pb.rs.Intn(4); i++ { 199 | t := pb.RandFuncType() 200 | in.Methods = append(in.Methods, Method{&ast.Ident{Name: "M" + strconv.Itoa(i)}, t}) 201 | } 202 | return in 203 | } 204 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "math/rand" 7 | "os" 8 | "os/exec" 9 | "regexp" 10 | "runtime" 11 | "strings" 12 | "sync/atomic" 13 | "time" 14 | 15 | "github.com/ALTree/microsmith/microsmith" 16 | ) 17 | 18 | var BuildCount int64 19 | var CrashCount int64 20 | var KnownCount int64 21 | 22 | var ( 23 | archF = flag.String("arch", "", "GOARCHs to fuzz (comma separated list)") 24 | debugF = flag.Bool("debug", false, "Run microsmith in debug mode") 25 | singlePkgF = flag.Bool("singlepkg", false, "Generate single-package programs") 26 | nooptF = flag.Bool("noopt", false, "Compile with optimizations disabled") 27 | pF = flag.Int("p", 1, "Number of fuzzing workers") 28 | raceF = flag.Bool("race", false, "Compile with -race") 29 | ssacheckF = flag.Bool("ssacheck", false, "Compile with -d=ssa/check/on") 30 | binF = flag.String("bin", "", "Go toolchain to fuzz") 31 | workdirF = flag.String("work", "work", "Workdir for the fuzzing process") 32 | notpF = flag.Bool("notp", false, "Don't use type-parameters") 33 | expF = flag.String("exp", "", "GOEXPERIMENT") 34 | ) 35 | 36 | var archs []string 37 | 38 | func main() { 39 | 40 | flag.Parse() 41 | rand.Seed(int64(time.Now().UnixNano())) 42 | 43 | if *debugF { 44 | debugRun() 45 | os.Exit(0) 46 | } 47 | 48 | if *binF == "" { 49 | fmt.Println("-bin must be set") 50 | os.Exit(2) 51 | } 52 | 53 | if *raceF && runtime.GOOS == "windows" { 54 | fmt.Println("-race fuzzing is not supported on Windows") 55 | os.Exit(2) 56 | } 57 | 58 | tc := guessToolchain(*binF) 59 | if tc == "gc" && *archF == "" { 60 | fmt.Println("-arch must be set when fuzzing gc") 61 | os.Exit(2) 62 | } 63 | if tc != "gc" && *archF != "" { 64 | fmt.Println("-arch must not be set when not fuzzing gc") 65 | os.Exit(2) 66 | } 67 | 68 | if _, err := os.Stat(*binF); os.IsNotExist(err) { 69 | fmt.Printf("toolchain %v does not exist\n", *binF) 70 | os.Exit(2) 71 | } 72 | 73 | fz := microsmith.BuildOptions{ 74 | Toolchain: *binF, 75 | Noopt: *nooptF, 76 | Race: *raceF, 77 | Ssacheck: *ssacheckF, 78 | Experiment: *expF, 79 | } 80 | 81 | archs = strings.Split(*archF, ",") 82 | 83 | if tc == "gc" { 84 | for _, a := range archs { 85 | installDeps(a, fz) 86 | } 87 | } 88 | if *ssacheckF { 89 | fmt.Printf("ssacheck [seed = %v]\n", microsmith.CheckSeed) 90 | } 91 | 92 | // Create workdir if not already there 93 | if _, err := os.Stat(*workdirF); os.IsNotExist(err) { 94 | err := os.MkdirAll(*workdirF, os.ModePerm) 95 | if err != nil { 96 | fmt.Println(err) 97 | os.Exit(2) 98 | } 99 | } 100 | 101 | startTime := time.Now() 102 | 103 | for i := 1; i <= *pF; i++ { 104 | go Fuzz(fz) 105 | } 106 | 107 | ticker := time.Tick(30 * time.Second) 108 | for range ticker { 109 | fmt.Printf("Built %4d (%5.1f/min) | crashes: %v", 110 | atomic.LoadInt64(&BuildCount), 111 | float64(atomic.LoadInt64(&BuildCount))/time.Since(startTime).Minutes(), 112 | atomic.LoadInt64(&CrashCount), 113 | ) 114 | if kc := atomic.LoadInt64(&KnownCount); kc == 0 { 115 | fmt.Print("\n") 116 | } else { 117 | fmt.Printf(" (known: %v)\n", kc) 118 | } 119 | } 120 | 121 | select {} 122 | } 123 | 124 | var crashWhitelist = []*regexp.Regexp{ 125 | regexp.MustCompile("untyped bool"), 126 | } 127 | 128 | func Fuzz(bo microsmith.BuildOptions) { 129 | conf := microsmith.ProgramConf{ 130 | MultiPkg: !*singlePkgF, 131 | TypeParams: !*notpF, 132 | } 133 | 134 | for { 135 | gp := microsmith.NewProgram(conf) 136 | err := gp.WriteToDisk(*workdirF) 137 | if err != nil { 138 | fmt.Printf("Could not write program to disk: %s", err) 139 | os.Exit(2) 140 | } 141 | 142 | var known bool 143 | for _, arch := range archs { 144 | timeout := time.AfterFunc( 145 | 60*time.Second, 146 | func() { 147 | gp.MoveCrasher() 148 | fmt.Printf("%v took too long to compile [GOARCH=%v]\n", gp.Name(), arch) 149 | os.Exit(2) 150 | }, 151 | ) 152 | out, err := gp.Compile(arch, bo) 153 | timeout.Stop() 154 | 155 | if err != nil { 156 | for _, crash := range crashWhitelist { 157 | if crash.MatchString(out) { 158 | known = true 159 | break 160 | } 161 | } 162 | 163 | if known { 164 | atomic.AddInt64(&KnownCount, 1) 165 | break 166 | } 167 | 168 | atomic.AddInt64(&CrashCount, 1) 169 | if arch != "" { 170 | fmt.Printf("-- CRASH (%v) ----------------------------------------------\n", arch) 171 | } else { 172 | fmt.Printf("-- CRASH ---------------------------------------------------\n") 173 | } 174 | fmt.Println(fiveLines(out)) 175 | fmt.Println("------------------------------------------------------------") 176 | gp.MoveCrasher() 177 | break 178 | } 179 | } 180 | 181 | atomic.AddInt64(&BuildCount, int64(len(archs))) 182 | gp.DeleteSource() 183 | } 184 | } 185 | 186 | func debugRun() { 187 | conf := microsmith.ProgramConf{ 188 | MultiPkg: !*singlePkgF, 189 | TypeParams: !*notpF, 190 | } 191 | gp := microsmith.NewProgram(conf) 192 | err := gp.Check() 193 | fmt.Println(gp) 194 | if err != nil { 195 | fmt.Printf("Program failed typechecking with error:\n%s\n", err) 196 | os.Exit(2) 197 | } 198 | } 199 | 200 | func installDeps(arch string, bo microsmith.BuildOptions) { 201 | var cmd *exec.Cmd 202 | if bo.Race { 203 | cmd = exec.Command(bo.Toolchain, "install", "-race", "std") 204 | } else { 205 | cmd = exec.Command(bo.Toolchain, "install", "std") 206 | } 207 | 208 | env := os.Environ() 209 | goos := "linux" 210 | switch arch { 211 | case "wasm": 212 | goos = "js" 213 | case "386sf": 214 | arch = "386" 215 | env = append(env, "GO386=softfloat") 216 | case "amd64_v3": 217 | arch = "amd64" 218 | env = append(env, "GOAMD64=v3") 219 | } 220 | 221 | env = append(env, "GOOS="+goos, "GOARCH="+arch, "GODEBUG=installgoroot=all") 222 | 223 | if exp := bo.Experiment; exp != "" { 224 | env = append(env, "GOEXPERIMENT="+exp) 225 | } 226 | 227 | cmd.Env = env 228 | 229 | out, err := cmd.CombinedOutput() 230 | if err != nil { 231 | fmt.Printf("Installing dependencies failed with error:\n----\n%s\n%s\n----\n", out, err) 232 | os.Exit(2) 233 | } 234 | } 235 | 236 | func guessToolchain(bin string) string { 237 | switch { 238 | case strings.Contains(bin, "gcc"): 239 | return "gcc" 240 | case strings.Contains(bin, "tinygo"): 241 | return "tinygo" 242 | default: 243 | return "gc" 244 | } 245 | } 246 | 247 | func fiveLines(s string) string { 248 | nl := 0 249 | for i := range s { 250 | if s[i] == '\n' { 251 | nl++ 252 | } 253 | if nl == 5 { 254 | return s[:i] + "\n..." 255 | } 256 | } 257 | return s 258 | } 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | microsmith generates random (but always valid) Go programs that can be 3 | used to stress-test Go compilers. 4 | 5 | #### Bugs found 6 | 7 | ##### Go Compiler 8 | 9 | - [#23504 internal compiler error: panic during layout](https://github.com/golang/go/issues/23504) 10 | - [#23889 panic: branch too far on arm64](https://github.com/golang/go/issues/23889) 11 | - [#25006 compiler hangs on tiny program](https://github.com/golang/go/issues/25006) 12 | - [#25269 compiler crashes with "stuck in spanz loop" error on s390x with -N](https://github.com/golang/go/issues/25269) 13 | - [#25526 internal compiler error: expected branch at write barrier block](https://github.com/golang/go/issues/25516) 14 | - [#25741 internal compiler error: not lowered: v15, OffPtr PTR64 PTR64](https://github.com/golang/go/issues/25741) 15 | - [#25993 internal compiler error: bad AuxInt value with ssacheck enabled](https://github.com/golang/go/issues/25993) 16 | - [#26024 internal compiler error: expected successors of write barrier block to be plain](https://github.com/golang/go/issues/26024) 17 | - [#26043 internal compiler error: panic during prove](https://github.com/golang/go/issues/26043) 18 | - [#28055 compiler crashes with "VARDEF is not a top level statement" error](https://github.com/golang/go/issues/28055) 19 | - [#29215 internal compiler error: panic during lower](https://github.com/golang/go/issues/29215) 20 | - [#29218 internal compiler error: bad live variable at entry](https://github.com/golang/go/issues/29218) 21 | - [#30257 internal compiler error: panic during lower II](https://github.com/golang/go/issues/30257) 22 | - [#30679 internal compiler error: panic during lower (unreachable)](https://github.com/golang/go/issues/30679) 23 | - [#31915 internal compiler error: val is in reg but not live](https://github.com/golang/go/issues/31915) 24 | - [#32454 internal compiler error: bad live variable at entry II](https://github.com/golang/go/issues/32454) 25 | - [#33903 internal compiler error: panic during short circuit](https://github.com/golang/go/issues/33903) 26 | - [#34520 panic: First time we've seen filename](https://github.com/golang/go/issues/34520) 27 | - [#35157 internal compiler error: aliasing constant which is not registered](https://github.com/golang/go/issues/35157) 28 | - [#35695 panic: Assigning a bogus line to XPos with no file](https://github.com/golang/go/issues/35695) 29 | - [#38359 internal compiler error: can't encode a NaN in AuxInt field](https://github.com/golang/go/issues/38359) 30 | - [#38916 internal compiler error: schedule does not include all values](https://github.com/golang/go/issues/38916) 31 | - [#38946 panic: log2 of 0 on arm64](https://github.com/golang/go/issues/38946) 32 | - [#39472 internal compiler error: schedule does not include all values in block](https://github.com/golang/go/issues/39472) 33 | - [#39505 internal compiler error: Flag* ops should never make it to codegen](https://github.com/golang/go/issues/39505) 34 | - [#42587 illegal combination SRA ADDCON REG REG on mips](https://github.com/golang/go/issues/42587) 35 | - [#42610 internal compiler error: PPC64 shift arg mb out of range](https://github.com/golang/go/issues/42610) 36 | - [#43099 internal compiler error: panic during lower (integer divide by zero)](https://github.com/golang/go/issues/43099) 37 | - [#43701 panic: invalid memory address or nil pointer dereference](https://github.com/golang/go/issues/43701) 38 | - [#44465 internal compiler error: arg 1 does not dominate](https://github.com/golang/go/issues/44465) 39 | - [#45359 internal compiler error: FlagConstant op should never make it to codegen](https://github.com/golang/go/issues/45359) 40 | - [#45589 internal compiler error: Invalid PPC64 rotate mask](https://github.com/golang/go/issues/45589) 41 | - [#45693 internal compiler error: bad live variable at entry](https://github.com/golang/go/issues/45693) 42 | - [#47684 internal compiler error: misuse of CaptureName](https://github.com/golang/go/issues/47684) 43 | - [#47723 internal compiler error: .dict already has a location](https://github.com/golang/go/issues/47723) 44 | - [#48026 internal compiler error: arg does not dominate](https://github.com/golang/go/issues/48026) 45 | - [#48191 data race in compiler building generic program](https://github.com/golang/go/issues/48191) 46 | - [#49122 internal compiler error: can't find source for MOVBload](https://github.com/golang/go/issues/49122) 47 | - [#49242 internal compiler error: found illegal assignment](https://github.com/golang/go/issues/49242) 48 | - [#49249 internal compiler error: panic during expand calls](https://github.com/golang/go/issues/49249) 49 | - [#49282 internal compiler error: Op...LECall and OpDereference have mismatched mem](https://github.com/golang/go/issues/49282) 50 | - [#49378 internal compiler error: Expand calls interface data problem](https://github.com/golang/go/issues/49378) 51 | - [#49619 cmd/link: panic: index out of range](https://github.com/golang/go/issues/49619) 52 | - [#52124 internal compiler error: panic: unexpected type: 10](https://github.com/golang/go/issues/52124) 53 | - [#52907 internal compiler error: dictionary variable not captured by value](https://github.com/golang/go/issues/52907) 54 | - [#53018 internal compiler error: panic during lower in linux/loong64](https://github.com/golang/go/issues/53018) 55 | - [#53390 internal compiler error: assertion failed](https://github.com/golang/go/issues/53390) 56 | - [#53439 internal compiler error: found illegal assignment](https://github.com/golang/go/issues/53439) 57 | - [#54542 internal compiler error on mismatch between -p value and pkg name](https://github.com/golang/go/issues/54542) 58 | - [#58161 internal compiler error: two final stores](https://github.com/golang/go/issues/58161) 59 | - [#59174 internal compiler error: not lowered: v108, Zero SSA PTR SSA](https://github.com/golang/go/issues/59174) 60 | - [#60990 internal compiler error: mismatched mem](https://github.com/golang/go/issues/60990) 61 | - [#60991 internal compiler error: assertion failed](https://github.com/golang/go/issues/60991) 62 | - [#61041 internal compiler error: not lowered: v654, Int64Hi INT32 INT](https://github.com/golang/go/issues/61041) 63 | - [#61127 internal compiler error: panic on clear of empty struct slice](https://github.com/golang/go/issues/61127) 64 | - [#61486 internal compiler error: panic: builtins.go: assertion failed](https://github.com/golang/go/issues/61486) 65 | - [#61778 internal compiler error: value .autotmp_1 incorrectly live at entry](https://github.com/golang/go/issues/61778) 66 | - [#61819 16 bit-width, short branch too far on loong64](https://github.com/golang/go/issues/61819) 67 | - [#61895 internal compiler error: unexpected frame offset for open-coded defer](https://github.com/golang/go/issues/61895) 68 | - [#61908 internal compiler error: missed typecheck](https://github.com/golang/go/issues/61908) 69 | - [#61992 internal compiler error: startMem[b1] has different values](https://github.com/golang/go/issues/61992) 70 | - [#62515 internal compiler error: unused auto: .autotmp_4](https://github.com/golang/go/issues/62515) 71 | - [#62698 internal compiler error: Invalid PPC64 rotate mask](https://github.com/golang/go/issues/62698) 72 | - [#63378 internal compiler error on range over negative integer](https://github.com/golang/go/issues/63378) 73 | - [#63462 internal compiler error: panic during expand calls on ARM](https://github.com/golang/go/issues/63462) 74 | - [#68816 internal compiler error: panic during prove](https://github.com/golang/go/issues/68816) 75 | - [#73180 illegal combination ADD $8, ZR, R2 on arm64](https://github.com/golang/go/issues/73180) 76 | - [#75617 internal compiler error: bad conversion to untyped](https://github.com/golang/go/issues/75617) 77 | 78 | ##### gccgo 79 | 80 | - [#44383 internal compiler error: Segmentation fault during GIMPLE pass ealias](https://github.com/golang/go/issues/44383) 81 | - [#47130 internal compiler error: in write_equal_function](https://github.com/golang/go/issues/47130) 82 | - [#56109 gccgo rejects valid program](https://github.com/golang/go/issues/56109) 83 | - [#56113 internal compiler error: in gen_reg_rtx, at emit-rtl](https://github.com/golang/go/issues/56113) 84 | - [#63495 error: esc already decorated call](https://github.com/golang/go/issues/63495) 85 | 86 | 87 | ##### tinygo 88 | 89 | - [#1653 LLVM ERROR: Cannot select: intrinsic %llvm.coro.size](https://github.com/tinygo-org/tinygo/issues/1653) 90 | - [#2749 panic: integer divide by zero on array of zero-sized elems](https://github.com/tinygo-org/tinygo/issues/2749) 91 | - [#2777 panic: compiler SIGSEGVs in transform.OptimizeAllocs call tree](https://github.com/tinygo-org/tinygo/issues/2777) 92 | - [#3083 panic: unknown type: chan int](https://github.com/tinygo-org/tinygo/issues/3083) 93 | - [#4214 panic: interp: load out of bounds](https://github.com/tinygo-org/tinygo/issues/4214) 94 | - [#4441 panic: *ssa.opaqueType: deferStack](https://github.com/tinygo-org/tinygo/issues/4441) 95 | -------------------------------------------------------------------------------- /microsmith/program.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "go/ast" 8 | "go/importer" 9 | "go/parser" 10 | "go/printer" 11 | "go/token" 12 | "go/types" 13 | "log" 14 | "math/rand" 15 | "os" 16 | "os/exec" 17 | "strings" 18 | ) 19 | 20 | // ---------------------------------------------------------------- 21 | // ProgramBuilder 22 | // ---------------------------------------------------------------- 23 | 24 | type ProgramBuilder struct { 25 | conf ProgramConf 26 | pkgs []*PackageBuilder 27 | } 28 | 29 | func NewProgramBuilder(conf ProgramConf) *ProgramBuilder { 30 | return &ProgramBuilder{conf: conf} 31 | } 32 | 33 | func (pb *ProgramBuilder) NewPackage(pkg string) *Package { 34 | db := NewPackageBuilder(pb.conf, pkg, pb) 35 | pb.pkgs = append(pb.pkgs, db) 36 | var buf bytes.Buffer 37 | printer.Fprint(&buf, token.NewFileSet(), db.File()) 38 | src := bytes.ReplaceAll(buf.Bytes(), []byte("func "), []byte("\nfunc ")) 39 | return &Package{name: pkg, source: src} 40 | } 41 | 42 | // ---------------------------------------------------------------- 43 | // Program 44 | // ---------------------------------------------------------------- 45 | 46 | type Program struct { 47 | workdir string // directory where the Program files are written 48 | pkgs []*Package // the program's packages 49 | id uint64 // random id used in the Program's filenames 50 | } 51 | 52 | type Package struct { 53 | name string 54 | source []byte 55 | filename string 56 | path *os.File 57 | } 58 | 59 | type BuildOptions struct { 60 | Toolchain string 61 | Noopt, Race, Ssacheck bool 62 | Experiment string 63 | } 64 | 65 | var CheckSeed int 66 | 67 | func init() { 68 | CheckSeed = rand.Int() % 1e5 69 | } 70 | 71 | func NewProgram(conf ProgramConf) *Program { 72 | pb := NewProgramBuilder(conf) 73 | pg := &Program{ 74 | id: rand.Uint64(), 75 | pkgs: make([]*Package, 0), 76 | } 77 | 78 | if conf.MultiPkg { 79 | pg.pkgs = append(pg.pkgs, pb.NewPackage(fmt.Sprintf("a_%d", pg.id))) 80 | } 81 | 82 | // main has to be last because it calls functions from the other 83 | // packages, which need to already exist in order for it to see 84 | // them. 85 | pg.pkgs = append(pg.pkgs, pb.NewPackage("main")) 86 | 87 | return pg 88 | } 89 | 90 | func (prog *Program) WriteToDisk(path string) error { 91 | prog.workdir = path 92 | for i, pkg := range prog.pkgs { 93 | var fileName string 94 | if pkg.name == "main" { 95 | fileName = fmt.Sprintf("main_%v.go", prog.id) 96 | } else { 97 | fileName = fmt.Sprintf("%v.go", pkg.name) 98 | } 99 | 100 | fh, err := os.Create(path + "/" + fileName) 101 | defer fh.Close() 102 | if err != nil { 103 | return err 104 | } 105 | 106 | fh.Write(pkg.source) 107 | prog.pkgs[i].filename = fileName 108 | prog.pkgs[i].path = fh 109 | } 110 | return nil 111 | } 112 | 113 | // Check uses go/parser and go/types to parse and typecheck gp 114 | // in-memory. 115 | func (prog *Program) Check() error { 116 | if len(prog.pkgs) > 1 { 117 | if _, err := os.Stat("work"); os.IsNotExist(err) { 118 | err := os.MkdirAll("work", os.ModePerm) 119 | if err != nil { 120 | return err 121 | } 122 | } 123 | defer func() { os.RemoveAll("work") }() 124 | 125 | prog.WriteToDisk("work") 126 | tc := "go" 127 | if bin := os.Getenv("GO_TC"); bin != "" { 128 | tc = bin 129 | } 130 | msg, err := prog.Compile("amd64", BuildOptions{Toolchain: tc}) 131 | if err != nil { 132 | return errors.Join(err, errors.New(msg)) 133 | } 134 | return nil 135 | } 136 | 137 | pkg, fset := prog.pkgs[0], token.NewFileSet() 138 | f, err := parser.ParseFile(fset, pkg.filename, pkg.source, 0) 139 | if err != nil { 140 | return err // parse error 141 | } 142 | 143 | conf := types.Config{Importer: importer.Default()} 144 | _, err = conf.Check(pkg.filename, fset, []*ast.File{f}, nil) 145 | if err != nil { 146 | return err // typecheck error 147 | } 148 | 149 | return nil 150 | } 151 | 152 | // Compile uses the given toolchain to build gp. It assumes that gp's 153 | // source is already written to disk by Program.WriteToDisk. 154 | // 155 | // If the compilation subprocess exits with an error code, Compile 156 | // returns the error message printed by the toolchain and the 157 | // subprocess error code. 158 | func (prog *Program) Compile(arch string, bo BuildOptions) (string, error) { 159 | if len(prog.pkgs) == 0 { 160 | return "", errors.New("Program has no packages") 161 | } 162 | 163 | baseName := fmt.Sprintf("%v", prog.id) 164 | arcName := "main_" + baseName + ".o" 165 | 166 | switch { 167 | 168 | case strings.Contains(bo.Toolchain, "gccgo"): 169 | oFlag := "-O2" 170 | if bo.Noopt { 171 | oFlag = "-Og" 172 | } 173 | cmd := exec.Command(bo.Toolchain, oFlag, "-o", arcName, "main_"+baseName+".go") 174 | cmd.Dir = prog.workdir 175 | out, err := cmd.CombinedOutput() 176 | if err != nil { 177 | return string(out), err 178 | } 179 | 180 | case strings.Contains(bo.Toolchain, "tinygo"): 181 | oFlag := "s" 182 | if bo.Noopt { 183 | oFlag = "0" 184 | } 185 | cmd := exec.Command(bo.Toolchain, "build", "-opt", oFlag, "-o", arcName, "main_"+baseName+".go") 186 | cmd.Dir = prog.workdir 187 | out, err := cmd.CombinedOutput() 188 | if err != nil { 189 | return string(out), err 190 | } 191 | 192 | default: 193 | 194 | // Setup env variables 195 | env := os.Environ() 196 | if arch == "wasm" { 197 | env = append(env, "GOOS=js") 198 | } else { 199 | env = append(env, "GOOS=linux") 200 | } 201 | 202 | if arch == "386sf" { 203 | env = append(env, "GOARCH=386", "GO386=softfloat") 204 | } else if arch == "amd64_v3" { 205 | env = append(env, "GOARCH=amd64", "GOAMD64=v3") 206 | } else { 207 | env = append(env, "GOARCH="+arch) 208 | } 209 | 210 | if exp := bo.Experiment; exp != "" { 211 | env = append(env, "GOEXPERIMENT="+exp) 212 | } 213 | 214 | // Setup compile args 215 | buildArgs := []string{"tool", "compile"} 216 | if bo.Race { 217 | buildArgs = append(buildArgs, "-race") 218 | } 219 | if bo.Noopt { 220 | buildArgs = append(buildArgs, "-N", "-l") 221 | } 222 | if bo.Ssacheck { 223 | cs := fmt.Sprintf("-d=ssa/check/seed=%v", CheckSeed) 224 | buildArgs = append(buildArgs, cs) 225 | } 226 | 227 | // Compile 228 | for _, pkg := range prog.pkgs { 229 | var cmdArgs []string 230 | buildArgs = append(buildArgs, []string{"-p", pkg.name}...) 231 | if pkg.name == "main" { 232 | cmdArgs = append(buildArgs, "-I=.") 233 | cmdArgs = append(cmdArgs, pkg.filename) 234 | } else { 235 | cmdArgs = append(buildArgs, pkg.filename) 236 | } 237 | 238 | cmd := exec.Command(bo.Toolchain, cmdArgs...) 239 | cmd.Dir, cmd.Env = prog.workdir, env 240 | out, err := cmd.CombinedOutput() 241 | if err != nil { 242 | return string(out), err 243 | } 244 | } 245 | 246 | // Setup link args 247 | linkArgs := []string{"tool", "link", "-L=."} 248 | if bo.Race { 249 | linkArgs = append(linkArgs, "-race") 250 | } 251 | linkArgs = append(linkArgs, "-o", baseName, arcName) 252 | 253 | // Link 254 | cmd := exec.Command(bo.Toolchain, linkArgs...) 255 | cmd.Dir, cmd.Env = prog.workdir, env 256 | out, err := cmd.CombinedOutput() 257 | if err != nil { 258 | return string(out), err 259 | } 260 | } 261 | 262 | prog.DeleteBinaries() 263 | return "", nil 264 | } 265 | 266 | // DeleteBinaries deletes any binary file written on disk. 267 | func (prog *Program) DeleteBinaries() { 268 | basePath := prog.workdir + "/" 269 | for _, pkg := range prog.pkgs { 270 | var err error 271 | if pkg.name == "main" { 272 | err = os.Remove(basePath + fmt.Sprintf("main_%d.o", prog.id)) 273 | } else { 274 | err = os.Remove(basePath + pkg.name + ".o") 275 | } 276 | if err != nil { 277 | log.Printf("could not remove %s: %s", basePath+"_"+pkg.name+".o", err) 278 | } 279 | 280 | } 281 | 282 | // ignore error since some toolchains don't write a binary 283 | _ = os.Remove(basePath + fmt.Sprintf("%v", prog.id)) 284 | } 285 | 286 | // DeleteSource deletes all gp files. 287 | func (gp Program) DeleteSource() { 288 | for _, file := range gp.pkgs { 289 | _ = os.Remove(file.path.Name()) 290 | } 291 | } 292 | 293 | // Move gp's files in a workdir subfolder named "crash". 294 | func (gp Program) MoveCrasher() { 295 | fld := gp.workdir + "/crash" 296 | if _, err := os.Stat(fld); os.IsNotExist(err) { 297 | err := os.Mkdir(fld, os.ModePerm) 298 | if err != nil { 299 | fmt.Printf("Could not create crash folder: %v", err) 300 | os.Exit(2) 301 | } 302 | } 303 | 304 | for _, pkg := range gp.pkgs { 305 | err := os.Rename(pkg.path.Name(), fld+"/"+pkg.filename) 306 | if err != nil { 307 | fmt.Printf("Could not move crasher: %v", err) 308 | os.Exit(2) 309 | } 310 | } 311 | } 312 | 313 | func (prog *Program) String() string { 314 | var res string 315 | for _, pkg := range prog.pkgs { 316 | res += string(pkg.source) 317 | if len(prog.pkgs) > 1 { 318 | res += "\n--------------------------------------------------\n" 319 | } 320 | } 321 | return res 322 | } 323 | 324 | func (prog *Program) Name() string { 325 | return fmt.Sprintf("%v", prog.id) 326 | } 327 | -------------------------------------------------------------------------------- /microsmith/package.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/parser" 7 | "go/token" 8 | "math/rand" 9 | "strings" 10 | ) 11 | 12 | type PackageBuilder struct { 13 | pb *ProgramBuilder 14 | pkg string 15 | ctx *Context 16 | rs *rand.Rand 17 | sb *StmtBuilder 18 | eb *ExprBuilder 19 | baseTypes []Type 20 | typedepth int 21 | funcs []*ast.FuncDecl // top level funcs declared in the package 22 | } 23 | 24 | func NewPackageBuilder(conf ProgramConf, pkg string, progb *ProgramBuilder) *PackageBuilder { 25 | pb := PackageBuilder{ 26 | pkg: pkg, 27 | ctx: NewContext(conf), 28 | rs: rand.New(rand.NewSource(rand.Int63())), 29 | pb: progb, 30 | } 31 | 32 | // Initialize Context.Scope with predeclared and a few stdlib 33 | // functions 34 | scope := Scope{pb: &pb, vars: make([]Variable, 0, 64)} 35 | for _, f := range BuiltinsFuncs { 36 | scope.vars = append(scope.vars, Variable{f, &ast.Ident{Name: f.N}}) 37 | } 38 | for _, f := range StdlibFuncs { 39 | scope.vars = append(scope.vars, Variable{f, &ast.Ident{Name: f.N}}) 40 | } 41 | scope.vars = append(scope.vars, MakeAtomicFuncs()...) 42 | 43 | pb.ctx.scope = &scope 44 | 45 | // Create the Stmt and Expr builders 46 | pb.sb = NewStmtBuilder(&pb) 47 | pb.eb = NewExprBuilder(&pb) 48 | pb.sb.E = pb.eb // breaks circular dependency in sb and eb inits 49 | 50 | // Add predeclared base types 51 | pb.baseTypes = []Type{ 52 | BT{"bool"}, 53 | BT{"byte"}, 54 | BT{"int"}, 55 | BT{"int8"}, 56 | BT{"int16"}, 57 | BT{"int32"}, 58 | BT{"int64"}, 59 | BT{"uint32"}, 60 | BT{"uint64"}, 61 | BT{"uint"}, 62 | BT{"uintptr"}, 63 | BT{"float32"}, 64 | BT{"float64"}, 65 | BT{"complex128"}, 66 | BT{"rune"}, 67 | BT{"string"}, 68 | } 69 | if conf.TypeParams { 70 | pb.baseTypes = append(pb.baseTypes, BT{"any"}) 71 | } 72 | 73 | return &pb 74 | } 75 | 76 | func (pb *PackageBuilder) FuncDecl() *ast.FuncDecl { 77 | 78 | fd := &ast.FuncDecl{ 79 | Name: pb.FuncIdent(len(pb.funcs)), 80 | Type: &ast.FuncType{ 81 | Func: 0, 82 | TypeParams: nil, 83 | Params: nil, 84 | Results: &ast.FieldList{}, 85 | }, 86 | } 87 | 88 | // choose function return types, at random 89 | returnTypes := []Type{} 90 | for i := 0; i < pb.rs.Intn(6); i++ { 91 | typ := RandItem(pb.rs, pb.baseTypes) 92 | fd.Type.Results.List = append( 93 | fd.Type.Results.List, 94 | &ast.Field{Type: &ast.Ident{Name: typ.Name()}}, 95 | ) 96 | returnTypes = append(returnTypes, typ) 97 | } 98 | 99 | // if we're not using type parameters, generate a body and return 100 | if !pb.Conf().TypeParams { 101 | fd.Body = pb.sb.FuncBody(returnTypes) 102 | return fd 103 | } 104 | 105 | // If we're using type parameters, use a few of the available ones 106 | // in the function signature, and add them to body's scope. 107 | tp, tps := Scope{pb: pb, vars: make([]Variable, 0, 8)}, []*ast.Field{} 108 | tpDecl, tpVars := []ast.Stmt{}, []*ast.Ident{} 109 | for i := 0; i < 1+pb.rs.Intn(8); i++ { 110 | ident := &ast.Ident{Name: fmt.Sprintf("G%v", i)} 111 | typ := RandItem(pb.rs, pb.ctx.constraints) 112 | tps = append( 113 | tps, 114 | &ast.Field{Names: []*ast.Ident{ident}, Type: typ.N}, 115 | ) 116 | tp.AddVariable(ident, typ) 117 | 118 | // Collect DeclStmts of variables of the typeparameter's type, 119 | // like this: 120 | // 121 | // var g0 G0 122 | // 123 | // Later we'll put these declarations at the beginning of the 124 | // function body, so we can use g0 when we need an expression 125 | // of type G0 but its type doesn't allow to create literals 126 | // (for example interface{ int | []int }). 127 | st, nv := pb.sb.DeclStmt(1, TypeParam{ident, typ}) 128 | tpVars = append(tpVars, nv...) 129 | tpDecl = append(tpDecl, st) 130 | } 131 | pb.ctx.typeparams = &tp 132 | 133 | fd.Type.TypeParams = &ast.FieldList{List: tps} 134 | 135 | // Generate the function body. We can't use FuncBody() here 136 | // because later we'll need to append more statements to the body, 137 | // but the ReturnStmt needs to be last. We'll add it manually. 138 | body := pb.sb.BlockStmt() 139 | 140 | // put the collected DeclStmts at the top of the body 141 | body.List = append(tpDecl, body.List...) 142 | 143 | // then append _ = ... to avoid unused variables errors. 144 | if len(tpVars) > 0 { 145 | body.List = append(body.List, pb.sb.UseVars(tpVars)) 146 | } 147 | 148 | // append the return statement (needs to be before removing tpVars 149 | // from scope because we may need to return one) 150 | body.List = append(body.List, pb.sb.ReturnStmt(returnTypes)) 151 | 152 | // finally, delete the typeparam vars from the scope. 153 | for _, v := range tpVars { 154 | pb.sb.S.DeleteIdentByName(v) 155 | } 156 | 157 | fd.Body = body 158 | 159 | // Type parameters are only available inside the function body, so 160 | // clear them out when we're done generating the body. 161 | pb.ctx.typeparams = nil 162 | 163 | return fd 164 | } 165 | 166 | func (pb *PackageBuilder) FuncIdent(i int) *ast.Ident { 167 | id := new(ast.Ident) 168 | id.Obj = &ast.Object{ 169 | Kind: ast.Fun, 170 | Name: fmt.Sprintf("F%v", i), 171 | } 172 | id.Name = id.Obj.Name 173 | 174 | return id 175 | } 176 | 177 | func (pb *PackageBuilder) Conf() ProgramConf { 178 | return pb.ctx.programConf 179 | } 180 | 181 | func (pb *PackageBuilder) Scope() *Scope { 182 | return pb.ctx.scope 183 | } 184 | 185 | func (pb *PackageBuilder) File() *ast.File { 186 | af := new(ast.File) 187 | af.Name = &ast.Ident{0, pb.pkg, nil} 188 | af.Decls = []ast.Decl{} 189 | 190 | if pb.pkg == "main" && pb.Conf().MultiPkg { 191 | for _, p := range pb.pb.pkgs { 192 | if p.pkg == "main" { 193 | continue 194 | } 195 | af.Decls = append(af.Decls, MakeImport(p.pkg)) 196 | } 197 | } 198 | 199 | pkgs := []string{"sync/atomic", "math", "reflect", "strings", "unsafe", "slices"} 200 | for _, p := range pkgs { 201 | af.Decls = append(af.Decls, MakeImport(p)) 202 | } 203 | for _, p := range pkgs { 204 | af.Decls = append(af.Decls, MakeUsePakage(p)) 205 | } 206 | 207 | if pb.Conf().TypeParams { 208 | for i := 0; i < 1+pb.rs.Intn(6); i++ { 209 | c, tp := pb.MakeRandConstraint(fmt.Sprintf("I%v", i)) 210 | af.Decls = append(af.Decls, c) 211 | pb.ctx.constraints = append(pb.ctx.constraints, tp) 212 | } 213 | } 214 | 215 | // Outside any func: 216 | // var i int 217 | // So we always have an int variable in scope. 218 | af.Decls = append(af.Decls, MakeInt()) 219 | pb.Scope().AddVariable(&ast.Ident{Name: "i"}, BT{"int"}) 220 | 221 | // half a dozen top-level variables 222 | for i := 1; i <= 6; i++ { 223 | t := pb.RandType() 224 | af.Decls = append(af.Decls, pb.MakeVar(t, i)) 225 | pb.Scope().AddVariable(&ast.Ident{Name: fmt.Sprintf("V%v", i)}, t) 226 | } 227 | 228 | // Declare top-level functions 229 | for i := 0; i < 4+pb.rs.Intn(5); i++ { 230 | fd := pb.FuncDecl() 231 | 232 | // append the function (decl and body) to the file 233 | af.Decls = append(af.Decls, fd) 234 | 235 | // save pointer to the decl in funcs, so we can list the 236 | // top level functions withoup having to loop on the whole 237 | // ast.File looking for func ast objects. 238 | pb.funcs = append(pb.funcs, fd) 239 | } 240 | 241 | // If we're not building the main package, we're done. 242 | if pb.pkg != "main" { 243 | return af 244 | } 245 | 246 | // build a main function 247 | mainF := &ast.FuncDecl{ 248 | Name: &ast.Ident{Name: "main"}, 249 | Type: &ast.FuncType{Params: &ast.FieldList{}}, 250 | Body: &ast.BlockStmt{}, 251 | } 252 | 253 | // call all the functions we declared 254 | for _, p := range pb.pb.pkgs { 255 | mainF.Body.List = append(mainF.Body.List, p.MakeFuncCalls()...) 256 | } 257 | 258 | af.Decls = append(af.Decls, mainF) 259 | return af 260 | } 261 | 262 | // Returns a slice of ast.ExprStms with calls to every top-level 263 | // function of the receiver. Takes care of adding explicit type 264 | // parameters, when the function has them. 265 | func (p *PackageBuilder) MakeFuncCalls() []ast.Stmt { 266 | calls := make([]ast.Stmt, 0, len(p.funcs)) 267 | for _, f := range p.funcs { 268 | var ce ast.CallExpr 269 | ce.Fun = f.Name 270 | 271 | // prepend to F() 272 | if p.pkg != "main" { 273 | ce.Fun = &ast.SelectorExpr{ 274 | X: &ast.Ident{Name: p.pkg}, 275 | Sel: f.Name, 276 | } 277 | } 278 | 279 | // instantiate type parameters 280 | if p.Conf().TypeParams { 281 | var indices []ast.Expr 282 | for _, typ := range f.Type.TypeParams.List { 283 | types := FindByName(p.ctx.constraints, typ.Type.(*ast.Ident).Name).Types 284 | indices = append(indices, RandItem(p.rs, types).Ast()) 285 | } 286 | ce.Fun = &ast.IndexListExpr{X: ce.Fun, Indices: indices} 287 | } 288 | 289 | calls = append(calls, &ast.ExprStmt{&ce}) 290 | } 291 | return calls 292 | } 293 | 294 | // Builds this: 295 | // 296 | // import "p" 297 | // 298 | // p must already include the surrounding "s. 299 | func MakeImport(p string) *ast.GenDecl { 300 | return &ast.GenDecl{ 301 | Tok: token.IMPORT, 302 | Specs: []ast.Spec{ 303 | &ast.ImportSpec{ 304 | Path: &ast.BasicLit{Kind: token.STRING, Value: "`" + p + "`"}, 305 | }, 306 | }, 307 | } 308 | } 309 | 310 | func MakeUsePakage(p string) *ast.GenDecl { 311 | m := map[string]struct{ p, f, v string }{ 312 | "unsafe": {"unsafe", "Sizeof", "0"}, 313 | "sync/atomic": {"atomic", "LoadInt32", "nil"}, 314 | "slices": {"slices", "All", "[]int{}"}, 315 | "math": {"math", "Sqrt", "0"}, 316 | "strings": {"strings", "Title", `""`}, 317 | "reflect": {"reflect", "DeepEqual", "1,1"}, 318 | } 319 | return &ast.GenDecl{ 320 | Tok: token.VAR, 321 | Specs: []ast.Spec{ 322 | &ast.ValueSpec{ 323 | Names: []*ast.Ident{&ast.Ident{Name: "_"}}, 324 | Values: []ast.Expr{&ast.CallExpr{ 325 | Fun: &ast.SelectorExpr{ 326 | X: &ast.Ident{Name: m[p].p}, 327 | Sel: &ast.Ident{Name: m[p].f}, 328 | }, 329 | Args: []ast.Expr{&ast.Ident{Name: m[p].v}}, 330 | }}, 331 | }, 332 | }, 333 | } 334 | } 335 | 336 | func MakeInt() *ast.GenDecl { 337 | return &ast.GenDecl{ 338 | Tok: token.VAR, 339 | Specs: []ast.Spec{ 340 | &ast.ValueSpec{ 341 | Names: []*ast.Ident{ 342 | &ast.Ident{Name: "i"}, 343 | }, 344 | Type: &ast.Ident{Name: "int"}, 345 | }, 346 | }, 347 | } 348 | } 349 | 350 | func (pb *PackageBuilder) MakeRandConstraint(name string) (*ast.GenDecl, Constraint) { 351 | var types []Type 352 | for len(types) < 1+pb.rs.Intn(8) { 353 | t := pb.RandType() 354 | name := t.Name() 355 | if strings.Contains(name, "int32") { // conflicts with rune 356 | // t.Contains() doesn't work with map[int32] because 357 | // Contains only looks at map values, not keys. 358 | continue 359 | } 360 | if strings.Contains(name, "interface") { 361 | continue 362 | } 363 | same := false 364 | for _, t2 := range types { 365 | if t.Equal(t2) { 366 | same = true 367 | break 368 | } 369 | } 370 | if !same { 371 | types = append(types, t) 372 | } 373 | } 374 | 375 | src := "package p\n" 376 | src += "type " + name + " interface{\n" 377 | for _, t := range types { 378 | if pb.rs.Intn(3) == 0 && t.Name() != "any" { 379 | src += "~" 380 | } 381 | src += t.Name() + "|" 382 | } 383 | src = strings.TrimRight(src, "|") 384 | src += "\n}" 385 | 386 | f, err := parser.ParseFile(token.NewFileSet(), "", src, 0) 387 | if err != nil { 388 | panic("Parsing constraint failed:\n" + src + "\n\n" + err.Error()) 389 | } 390 | decl := f.Decls[0].(*ast.GenDecl) 391 | 392 | return decl, Constraint{Types: types, N: &ast.Ident{Name: name}} 393 | } 394 | 395 | func (pb *PackageBuilder) MakeVar(t Type, i int) *ast.GenDecl { 396 | return &ast.GenDecl{ 397 | Tok: token.VAR, 398 | Specs: []ast.Spec{ 399 | &ast.ValueSpec{ 400 | Names: []*ast.Ident{ 401 | &ast.Ident{Name: fmt.Sprintf("V%v", i)}, 402 | }, 403 | Type: t.Ast(), 404 | Values: []ast.Expr{ 405 | pb.eb.Expr(t), 406 | }, 407 | }, 408 | }, 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /microsmith/stmt.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/token" 7 | "math/rand" 8 | ) 9 | 10 | // -------------------------------- 11 | // StmtBuilder type 12 | // -------------------------------- 13 | 14 | type StmtBuilder struct { 15 | pb *PackageBuilder 16 | 17 | C *Context 18 | E *ExprBuilder 19 | R *rand.Rand 20 | S *Scope 21 | 22 | // TODO(alb): move all of these into Context or PackageBuilder 23 | depth int // how deep the stmt hyerarchy is 24 | funcp int // counter for function param names 25 | labels []string 26 | label int // counter for labels names 27 | } 28 | 29 | func NewStmtBuilder(pb *PackageBuilder) *StmtBuilder { 30 | return &StmtBuilder{ 31 | pb: pb, 32 | C: pb.ctx, 33 | E: nil, // this hasn't been created yet 34 | R: pb.rs, 35 | S: pb.ctx.scope, 36 | } 37 | } 38 | 39 | // -------------------------------- 40 | // Builder Methods 41 | // -------------------------------- 42 | 43 | // Returns true if the block statement currently being built is 44 | // allowed to have statements nested inside it. 45 | func (sb *StmtBuilder) CanNest() bool { 46 | return (sb.depth <= 3) && (sb.R.Float64() < 0.8) 47 | } 48 | 49 | func (sb *StmtBuilder) Stmt() ast.Stmt { 50 | if !sb.CanNest() { 51 | return sb.AssignStmt() 52 | } 53 | 54 | switch sb.R.Intn(12) { 55 | case 0: 56 | return sb.AssignStmt() 57 | case 1: 58 | return sb.BlockStmt() 59 | case 2: 60 | if sb.R.Intn(2) == 0 { // for range 61 | return sb.RangeStmt() 62 | } 63 | if sb.R.Intn(4) == 0 { // labelled plain for 64 | sb.label++ 65 | label := fmt.Sprintf("lab%v", sb.label) 66 | sb.labels = append(sb.labels, label) 67 | fs := &ast.LabeledStmt{ 68 | Label: &ast.Ident{Name: label}, 69 | Stmt: sb.ForStmt(), 70 | } 71 | return fs 72 | } 73 | return sb.ForStmt() // plain for 74 | case 3: 75 | return sb.IfStmt() 76 | case 4: 77 | return sb.SwitchStmt() 78 | case 5: 79 | return sb.SendStmt() 80 | case 6: 81 | return sb.SelectStmt() 82 | case 7: 83 | if sb.C.inLoop { 84 | return sb.BranchStmt() 85 | } 86 | return sb.AssignStmt() 87 | case 8: 88 | return sb.DeferStmt() 89 | case 9: 90 | return sb.GoStmt() 91 | case 10: 92 | return sb.ExprStmt() 93 | case 11: 94 | return sb.ClearStmt() 95 | default: 96 | panic("unreachable") 97 | } 98 | } 99 | 100 | // gets a random variable currently in scope (that we can assign to), 101 | // and builds an AssignStmt with a random Expr of its type on the RHS 102 | func (sb *StmtBuilder) AssignStmt() *ast.AssignStmt { 103 | v, ok := sb.S.RandAssignable() 104 | if !ok { 105 | fmt.Println(sb.S) 106 | panic("No assignable variable in scope") 107 | } 108 | 109 | switch t := v.Type.(type) { 110 | 111 | case StructType: 112 | // For structs, 50/50 between assigning to the variable and 113 | // setting one of its fields. 114 | if sb.R.Intn(2) == 0 || len(t.Ftypes) == 0 { 115 | // v = struct{, , ...} 116 | return &ast.AssignStmt{ 117 | Lhs: []ast.Expr{v.Name}, 118 | Tok: token.ASSIGN, 119 | Rhs: []ast.Expr{sb.E.CompositeLit(t)}, 120 | } 121 | } else { 122 | // v.field = 123 | // 124 | // we need to avoid getting to a chan receive here, i.e. we 125 | // need to avoid generating: 126 | // 127 | // <-st.c = ... 128 | // 129 | // for a st struct{ c chan int } (at any depth), because 130 | // that will fail to compile with error: 131 | // 132 | // cannot assign to <-st.c (comma, ok expression of type int) 133 | // 134 | // So, only in AssignStmt, never go deeper than 1 level 135 | // inside structs, and assign directly to a depth-1 field 136 | // (that is not a chan). 137 | // 138 | // TODO(alb): this can be changed to go to arbitrary-depth 139 | // as long as it avoids channels. Doable? 140 | fis := make([]int, 0, len(t.Ftypes)) 141 | for i, ft := range t.Ftypes { 142 | if _, ok := ft.(ChanType); !ok { 143 | fis = append(fis, i) 144 | } 145 | } 146 | 147 | if len(fis) == 0 { // fallback to v = struct{ ... } 148 | return &ast.AssignStmt{ 149 | Lhs: []ast.Expr{v.Name}, 150 | Tok: token.ASSIGN, 151 | Rhs: []ast.Expr{sb.E.CompositeLit(t)}, 152 | } 153 | } 154 | 155 | fi := RandItem(sb.pb.rs, fis) 156 | return &ast.AssignStmt{ 157 | Lhs: []ast.Expr{&ast.SelectorExpr{X: v.Name, Sel: &ast.Ident{Name: t.Fnames[fi]}}}, 158 | Tok: token.ASSIGN, 159 | Rhs: []ast.Expr{sb.E.Expr(t.Ftypes[fi])}, 160 | } 161 | } 162 | 163 | case ArrayType: 164 | // For arrays, 50/50 between 165 | // A[] = 166 | // A = { , , ... } 167 | if sb.R.Intn(2) == 0 { 168 | return &ast.AssignStmt{ 169 | Lhs: []ast.Expr{sb.E.IndexExpr(v.Name)}, 170 | Tok: token.ASSIGN, 171 | Rhs: []ast.Expr{sb.E.Expr(t.Base())}, 172 | } 173 | } else { 174 | return &ast.AssignStmt{ 175 | Lhs: []ast.Expr{v.Name}, 176 | Tok: token.ASSIGN, 177 | Rhs: []ast.Expr{sb.E.Expr(v.Type)}, 178 | } 179 | } 180 | 181 | case MapType: 182 | // For maps, 50/50 between 183 | // M[] = 184 | // M = { : } 185 | if sb.R.Intn(2) == 0 { 186 | return &ast.AssignStmt{ 187 | Lhs: []ast.Expr{sb.E.MapIndexExpr(v.Name, v.Type.(MapType).KeyT)}, 188 | Tok: token.ASSIGN, 189 | Rhs: []ast.Expr{sb.E.Expr(v.Type.(MapType).ValueT)}, 190 | } 191 | } else { 192 | return &ast.AssignStmt{ 193 | Lhs: []ast.Expr{v.Name}, 194 | Tok: token.ASSIGN, 195 | Rhs: []ast.Expr{sb.E.Expr(v.Type)}, 196 | } 197 | } 198 | 199 | default: 200 | return &ast.AssignStmt{ 201 | Lhs: []ast.Expr{v.Name}, 202 | Tok: token.ASSIGN, 203 | Rhs: []ast.Expr{sb.E.Expr(v.Type)}, 204 | } 205 | } 206 | } 207 | 208 | // returns a continue/break statement 209 | func (sb *StmtBuilder) BranchStmt() *ast.BranchStmt { 210 | var bs ast.BranchStmt 211 | 212 | bs.Tok = RandItem(sb.R, []token.Token{token.GOTO, token.CONTINUE, token.BREAK}) 213 | 214 | // break/continue/goto to a label with chance 0.25 215 | if len(sb.labels) > 0 && sb.R.Intn(4) == 0 { 216 | li := sb.R.Intn(len(sb.labels)) 217 | bs.Label = &ast.Ident{Name: sb.labels[li]} 218 | sb.labels = append(sb.labels[:li], sb.labels[li+1:]...) 219 | } else { 220 | // If we didn't add a label, GOTO is not allowed. 221 | if sb.R.Intn(2) == 0 { 222 | bs.Tok = token.BREAK 223 | } else { 224 | bs.Tok = token.CONTINUE 225 | } 226 | } 227 | 228 | return &bs 229 | } 230 | 231 | // BlockStmt returns a new Block Statement. The returned Stmt is 232 | // always a valid block. It up to BlockStmt's caller to make sure 233 | // BlockStmt is only called when we have not yet reached max depth. 234 | func (sb *StmtBuilder) BlockStmt() *ast.BlockStmt { 235 | 236 | sb.depth++ 237 | defer func() { sb.depth-- }() 238 | 239 | bs := new(ast.BlockStmt) 240 | stmts := []ast.Stmt{} 241 | 242 | // A new block means opening a new scope. Declare a few new vars 243 | // of random types. 244 | var newVars []*ast.Ident 245 | for _, t := range sb.pb.RandTypes(3 + sb.R.Intn(6)) { 246 | newDecl, nv := sb.DeclStmt(1+sb.R.Intn(3), t) 247 | stmts = append(stmts, newDecl) 248 | newVars = append(newVars, nv...) 249 | } 250 | 251 | var nStmts int 252 | if !sb.CanNest() { 253 | // If we stop nesting statements, guarantee 8 assignmens, to 254 | // so we don't generate almost-empty blocks. 255 | nStmts = 8 256 | } else { 257 | nStmts = 4 + sb.R.Intn(5) 258 | } 259 | 260 | // Fill the block's body. 261 | for i := 0; i < nStmts; i++ { 262 | stmts = append(stmts, sb.Stmt()) 263 | } 264 | 265 | if len(newVars) > 0 { 266 | stmts = append(stmts, sb.UseVars(newVars)) 267 | } 268 | 269 | for _, v := range newVars { 270 | sb.S.DeleteIdentByName(v) 271 | } 272 | 273 | bs.List = stmts 274 | return bs 275 | } 276 | 277 | // FuncBody returns a BlockStmt, like BlockStmt, except it appends a 278 | // ReturnStmt of the given types at the end. 279 | func (sb *StmtBuilder) FuncBody(t []Type) *ast.BlockStmt { 280 | b := sb.BlockStmt() 281 | if len(t) > 0 { 282 | b.List = append(b.List, sb.ReturnStmt(t)) 283 | } 284 | return b 285 | } 286 | 287 | // DeclStmt returns a DeclStmt where nVars new variables of type kind 288 | // are declared, and a list of the newly created *ast.Ident that 289 | // entered the scope. 290 | func (sb *StmtBuilder) DeclStmt(nVars int, t Type) (*ast.DeclStmt, []*ast.Ident) { 291 | if nVars < 1 { 292 | panic("nVars < 1") 293 | } 294 | 295 | if _, ok := t.(FuncType); ok { 296 | nVars = 1 297 | } 298 | 299 | gd := new(ast.GenDecl) 300 | gd.Tok = token.VAR 301 | 302 | // generate the type specifier 303 | var typ ast.Expr 304 | var rhs []ast.Expr 305 | 306 | switch t2 := t.(type) { 307 | case BasicType, ArrayType, PointerType, StructType, ChanType, MapType, InterfaceType: 308 | typ = t2.Ast() 309 | 310 | case FuncType: 311 | // For function we don't just declare the variable, we also 312 | // assign to it (so we can give the function a body): 313 | // 314 | // var FNC0 func(int) int = func(p0 int, p1 bool) int { 315 | // Stmts ... 316 | // return 317 | // } 318 | // 319 | // But 10% of the times we don't (and the func variable will 320 | // be nil). 321 | 322 | // First off all, remove all the labels currently in scope. 323 | // The Go Specification says: 324 | // 325 | // The scope of a label is the body of the function in which 326 | // it is declared and excludes the body of any nested 327 | // function. 328 | // 329 | // So the nested function we're about to create cannot use 330 | // labels created outside its body. 331 | oldLabels := sb.labels 332 | sb.labels = []string{} 333 | 334 | // LHS is the type specifier for the given FuncType, with no 335 | // parameter names 336 | p, r := t2.MakeFieldLists(false, 0) 337 | typ = &ast.FuncType{Params: p, Results: r} 338 | 339 | // RHS (with chance 0.9) 340 | 341 | if sb.R.Intn(10) != 0 { 342 | // Func type specifier again, but this time with parameter 343 | // names 344 | p, r = t2.MakeFieldLists(true, sb.funcp) 345 | fl := &ast.FuncLit{ 346 | Type: &ast.FuncType{Params: p, Results: r}, 347 | Body: &ast.BlockStmt{}, 348 | } 349 | 350 | // add the parameters to the scope 351 | for i, param := range p.List { 352 | if ep, ok := t2.Args[i].(EllipsisType); ok { 353 | sb.S.AddVariable(param.Names[0], ArrayOf(ep.Base)) 354 | } else { 355 | sb.S.AddVariable(param.Names[0], t2.Args[i]) 356 | } 357 | 358 | sb.funcp++ 359 | } 360 | 361 | // generate a function body 362 | sb.depth++ 363 | if sb.CanNest() { 364 | old := sb.C.inLoop 365 | sb.C.inLoop = false 366 | defer func() { sb.C.inLoop = old }() 367 | fl.Body = sb.BlockStmt() 368 | } else { 369 | n := 2 + sb.R.Intn(3) 370 | stl := make([]ast.Stmt, 0, n) 371 | for i := 0; i < n; i++ { 372 | stl = append(stl, sb.AssignStmt()) 373 | } 374 | fl.Body = &ast.BlockStmt{List: stl} 375 | } 376 | sb.depth-- 377 | 378 | // Finally, append a closing return statement 379 | retStmt := &ast.ReturnStmt{Results: []ast.Expr{}} 380 | for _, ret := range t2.Ret { 381 | retStmt.Results = append(retStmt.Results, sb.E.Expr(ret)) 382 | } 383 | fl.Body.List = append(fl.Body.List, retStmt) 384 | rhs = append(rhs, fl) 385 | 386 | // remove the function parameters from scope... 387 | for _, param := range fl.Type.Params.List { 388 | sb.S.DeleteIdentByName(param.Names[0]) 389 | sb.funcp-- 390 | } 391 | } 392 | // and restore the labels. 393 | sb.labels = oldLabels 394 | 395 | case TypeParam: 396 | typ = t2.Ast() 397 | 398 | default: 399 | panic("DeclStmt bad type " + t.Name()) 400 | } 401 | 402 | idents := make([]*ast.Ident, 0, nVars) 403 | for i := 0; i < nVars; i++ { 404 | idents = append(idents, sb.S.NewIdent(t)) 405 | } 406 | 407 | gd.Specs = []ast.Spec{ 408 | &ast.ValueSpec{ 409 | Names: idents, 410 | Type: typ, 411 | Values: rhs, 412 | }, 413 | } 414 | 415 | ds := new(ast.DeclStmt) 416 | ds.Decl = gd 417 | 418 | return ds, idents 419 | } 420 | 421 | // Like DeclStmt, but declares a variable of a random TypeParam. 422 | func (sb *StmtBuilder) DeclStmtTypeParam(i int) (*ast.DeclStmt, []*ast.Ident) { 423 | gd := new(ast.GenDecl) 424 | gd.Tok = token.VAR 425 | name := fmt.Sprintf("x%v", i) 426 | gd.Specs = []ast.Spec{ 427 | &ast.ValueSpec{ 428 | Names: []*ast.Ident{&ast.Ident{Name: name}}, 429 | Type: nil, 430 | Values: nil, 431 | }, 432 | } 433 | 434 | idents := []*ast.Ident{&ast.Ident{Name: name}} 435 | ds := new(ast.DeclStmt) 436 | ds.Decl = gd 437 | return ds, idents 438 | } 439 | 440 | func (sb *StmtBuilder) ForStmt() *ast.ForStmt { 441 | 442 | sb.depth++ 443 | defer func() { sb.depth-- }() 444 | 445 | var fs ast.ForStmt 446 | // - Cond stmt with chance 0.94 (1-1/16) 447 | // - Init and Post statements with chance 0.5 448 | // - A body with chance 0.97 (1-1/32) 449 | if sb.R.Intn(16) > 0 { 450 | fs.Cond = sb.E.Expr(BT{"bool"}) 451 | } 452 | if sb.R.Intn(2) > 0 { 453 | fs.Init = sb.AssignStmt() 454 | } 455 | if sb.R.Intn(2) > 0 { 456 | fs.Post = sb.AssignStmt() 457 | } 458 | if sb.R.Intn(32) > 0 { 459 | old := sb.C.inLoop 460 | sb.C.inLoop = false 461 | defer func() { sb.C.inLoop = old }() 462 | fs.Body = sb.BlockStmt() 463 | } else { 464 | // empty loop body 465 | fs.Body = &ast.BlockStmt{} 466 | } 467 | 468 | // consume all active labels to avoid unused compilation errors 469 | for _, l := range sb.labels { 470 | fs.Body.List = append(fs.Body.List, 471 | &ast.BranchStmt{ 472 | Tok: RandItem(sb.R, []token.Token{token.GOTO, token.BREAK, token.CONTINUE}), 473 | Label: &ast.Ident{Name: l}, 474 | }) 475 | } 476 | sb.labels = []string{} 477 | 478 | return &fs 479 | } 480 | 481 | func (sb *StmtBuilder) RangeStmt() *ast.RangeStmt { 482 | sb.depth++ 483 | old := sb.C.inLoop 484 | sb.C.inLoop = true 485 | defer func() { sb.depth--; sb.C.inLoop = old }() 486 | 487 | // it's either 488 | // k := range [int] 489 | // or 490 | // k, v := range [string or slice] 491 | // or 492 | // [k, v] := range [function] 493 | 494 | var k, v *ast.Ident 495 | var e ast.Expr 496 | 497 | f := sb.E.Expr 498 | if !sb.E.Deepen() { 499 | f = sb.E.VarOrLit 500 | } 501 | 502 | // randomly choose a type for the expression we range on 503 | switch sb.R.Intn(4) { 504 | case 0: // slice 505 | t := ArrayOf(sb.pb.RandType()) 506 | e = f(t) 507 | k = sb.S.NewIdent(BT{"int"}) 508 | v = sb.S.NewIdent(t.Base()) 509 | case 1: // string 510 | e = f(BT{"string"}) 511 | k = sb.S.NewIdent(BT{"int"}) 512 | v = sb.S.NewIdent(BT{"rune"}) 513 | case 2: // int 514 | e = f(BT{"int"}) 515 | k = sb.S.NewIdent(BT{"int"}) 516 | case 3: // func 517 | 518 | // 50/50 between generating a new Rangeable func type or a 519 | // call to a function from the slices package. 520 | if sb.R.Intn(2) == 0 { 521 | t := sb.pb.RandType() 522 | f := FuncType{N: "slices.All"} 523 | e = sb.E.CallFunction(Variable{f, &ast.Ident{Name: f.N}}, t) 524 | k = sb.S.NewIdent(BT{"int"}) 525 | v = sb.S.NewIdent(t) 526 | } else { 527 | 528 | ft := sb.pb.RandRangeableFuncType() 529 | p, r := ft.MakeFieldLists(true, sb.funcp) 530 | 531 | // add yield param to the scope 532 | sb.S.AddVariable(p.List[0].Names[0], ft.Args[0]) 533 | sb.funcp++ 534 | 535 | // generate a body for the func 536 | sb.depth++ 537 | var body *ast.BlockStmt 538 | if sb.CanNest() { 539 | old := sb.C.inLoop 540 | sb.C.inLoop = false 541 | body = sb.BlockStmt() 542 | sb.C.inLoop = old 543 | } else { 544 | body = &ast.BlockStmt{List: []ast.Stmt{sb.AssignStmt()}} 545 | } 546 | sb.depth-- 547 | 548 | e = &ast.FuncLit{ 549 | Type: &ast.FuncType{Params: p, Results: r}, 550 | Body: body, 551 | } 552 | 553 | // remove the yield param from the scope 554 | sb.S.DeleteIdentByName(p.List[0].Names[0]) 555 | sb.funcp-- 556 | 557 | // declare the iteration variables if needed 558 | switch args := ft.Args[0].(FuncType).Args; len(args) { 559 | case 1: 560 | k = sb.S.NewIdent(args[0]) 561 | case 2: 562 | k = sb.S.NewIdent(args[0]) 563 | v = sb.S.NewIdent(args[1]) 564 | } 565 | } 566 | default: 567 | panic("unreachable") 568 | } 569 | 570 | rs := &ast.RangeStmt{Tok: token.DEFINE, X: e, Body: sb.BlockStmt()} 571 | 572 | if k != nil { 573 | rs.Key = k 574 | rs.Body.List = append(rs.Body.List, sb.UseVars([]*ast.Ident{k})) 575 | sb.S.DeleteIdentByName(k) 576 | } 577 | 578 | if v != nil { 579 | rs.Value = v 580 | rs.Body.List = append(rs.Body.List, sb.UseVars([]*ast.Ident{v})) 581 | sb.S.DeleteIdentByName(v) 582 | } 583 | 584 | return rs 585 | } 586 | 587 | func (sb *StmtBuilder) DeferStmt() *ast.DeferStmt { 588 | if v, ok := sb.S.RandFunc(); ok && sb.R.Intn(4) > 0 { 589 | return &ast.DeferStmt{Call: sb.E.CallFunction(v)} 590 | } else { 591 | old := sb.C.inDefer 592 | sb.C.inDefer = true 593 | defer func() { sb.C.inDefer = old }() 594 | return &ast.DeferStmt{Call: sb.E.ConjureAndCallFunc(sb.pb.RandType())} 595 | } 596 | } 597 | 598 | func (sb *StmtBuilder) GoStmt() *ast.GoStmt { 599 | if v, ok := sb.S.RandFunc(); ok && sb.R.Intn(4) > 0 { 600 | return &ast.GoStmt{Call: sb.E.CallFunction(v)} 601 | } else { 602 | old := sb.C.inDefer 603 | sb.C.inDefer = true 604 | defer func() { sb.C.inDefer = old }() 605 | return &ast.GoStmt{Call: sb.E.ConjureAndCallFunc(sb.pb.RandType())} 606 | } 607 | } 608 | 609 | func (sb *StmtBuilder) IfStmt() *ast.IfStmt { 610 | 611 | sb.depth++ 612 | defer func() { sb.depth-- }() 613 | 614 | is := &ast.IfStmt{ 615 | Cond: sb.E.Expr(BT{"bool"}), 616 | Body: sb.BlockStmt(), 617 | } 618 | 619 | // optionally attach an else 620 | if sb.R.Intn(2) == 0 { 621 | is.Else = sb.BlockStmt() 622 | } 623 | 624 | return is 625 | } 626 | 627 | // ReturnStmt builds a return statement with expression of the given 628 | // types. 629 | func (sb *StmtBuilder) ReturnStmt(types []Type) *ast.ReturnStmt { 630 | sb.depth++ 631 | defer func() { sb.depth-- }() 632 | 633 | ret := &ast.ReturnStmt{Results: make([]ast.Expr, len(types))} 634 | 635 | for i, t := range types { 636 | ret.Results[i] = sb.E.Expr(t) 637 | } 638 | 639 | return ret 640 | 641 | } 642 | 643 | func (sb *StmtBuilder) SwitchStmt() *ast.SwitchStmt { 644 | sb.depth++ 645 | defer func() { sb.depth-- }() 646 | 647 | t := sb.pb.RandComparableType() 648 | if sb.R.Intn(2) == 0 && sb.S.Has(PointerOf(t)) { 649 | // sometimes switch on a pointer value 650 | t = PointerOf(t) 651 | } 652 | 653 | ss := &ast.SwitchStmt{ 654 | Tag: sb.E.Expr(t), 655 | Body: &ast.BlockStmt{List: []ast.Stmt{}}, 656 | } 657 | 658 | // add a few cases 659 | for i := 0; i < sb.R.Intn(4); i++ { 660 | cc, ok := sb.CaseClause(t, false) 661 | ss.Body.List = append(ss.Body.List, cc) 662 | if !ok { 663 | break 664 | } 665 | } 666 | 667 | // optionally add a default case 668 | if sb.R.Intn(3) != 0 { 669 | cc, _ := sb.CaseClause(t, true) 670 | ss.Body.List = append(ss.Body.List, cc) 671 | } 672 | return ss 673 | } 674 | 675 | // builds and returns a single CaseClause switching on type kind. If 676 | // def is true, returns a 'default' switch case. 677 | func (sb *StmtBuilder) CaseClause(t Type, def bool) (*ast.CaseClause, bool) { 678 | cc := new(ast.CaseClause) 679 | ret := true 680 | if !def { 681 | e, ok := sb.E.NonConstantExpr(t) 682 | if !ok { 683 | ret = false 684 | } 685 | cc.List = []ast.Expr{e} 686 | } 687 | cc.Body = sb.BlockStmt().List 688 | return cc, ret 689 | } 690 | 691 | func (sb *StmtBuilder) IncDecStmt(t Type) *ast.IncDecStmt { 692 | panic("not implemented") 693 | } 694 | 695 | func (sb *StmtBuilder) SendStmt() *ast.SendStmt { 696 | st := new(ast.SendStmt) 697 | if ch, ok := sb.S.RandChan(); !ok { 698 | t := sb.pb.RandType() 699 | st.Chan = sb.E.VarOrLit(ChanType{T: t}) 700 | st.Value = sb.E.Expr(t) 701 | } else { 702 | st.Chan = ch.Name 703 | st.Value = sb.E.Expr(ch.Type.(ChanType).Base()) 704 | } 705 | 706 | return st 707 | } 708 | 709 | func (sb *StmtBuilder) SelectStmt() *ast.SelectStmt { 710 | sb.depth++ 711 | defer func() { sb.depth-- }() 712 | 713 | ss := &ast.SelectStmt{ 714 | Body: &ast.BlockStmt{List: []ast.Stmt{}}, 715 | } 716 | 717 | for i := 0; i < sb.R.Intn(4); i++ { 718 | ss.Body.List = append(ss.Body.List, sb.CommClause(false)) 719 | } 720 | 721 | if sb.R.Intn(4) == 0 { 722 | ss.Body.List = append(ss.Body.List, sb.CommClause(true)) 723 | } 724 | 725 | return ss 726 | } 727 | 728 | // CommClause is the Select clause. This function returns: 729 | // 730 | // case <-c if def is false 731 | // default if def is true 732 | func (sb *StmtBuilder) CommClause(def bool) *ast.CommClause { 733 | 734 | // a couple of Stmt are enough for a select case body 735 | stmtList := []ast.Stmt{sb.Stmt(), sb.Stmt()} 736 | 737 | if def { 738 | return &ast.CommClause{Body: stmtList} 739 | } 740 | 741 | ch, chanInScope := sb.S.RandChan() 742 | if !chanInScope { 743 | // when no chan is in scope, we select from a newly made channel, 744 | // i.e. we build and return 745 | // select <-make(chan ) 746 | t := sb.pb.RandType() 747 | return &ast.CommClause{ 748 | Comm: &ast.ExprStmt{ 749 | X: &ast.UnaryExpr{ 750 | Op: token.ARROW, 751 | X: &ast.CallExpr{ 752 | Fun: &ast.Ident{Name: "make"}, 753 | Args: []ast.Expr{ 754 | &ast.ChanType{ 755 | Dir: 3, 756 | Value: t.Ast(), 757 | }, 758 | }, 759 | }, 760 | }, 761 | }, 762 | Body: stmtList, 763 | } 764 | } 765 | 766 | // otherwise, we receive from one of the channels in scope 767 | return &ast.CommClause{ 768 | Comm: &ast.ExprStmt{ 769 | X: sb.E.ChanReceiveExpr(ch.Name), 770 | }, 771 | Body: stmtList, 772 | } 773 | 774 | } 775 | 776 | func (sb *StmtBuilder) ExprStmt() *ast.ExprStmt { 777 | 778 | // Close(ch) or <-ch. 779 | if ch, ok := sb.S.RandChan(); ok && sb.R.Intn(4) == 0 { 780 | if sb.R.Intn(2) == 0 { 781 | return &ast.ExprStmt{ 782 | X: sb.E.ChanReceiveExpr(ch.Name), 783 | } 784 | } else { 785 | return &ast.ExprStmt{ 786 | X: &ast.CallExpr{ 787 | Fun: CloseIdent, 788 | Args: []ast.Expr{ch.Name}, 789 | }, 790 | } 791 | } 792 | } 793 | 794 | // Call a random function. We don't use RandCallExpr() because 795 | // that could choose a built-in (like len), which is not allowed 796 | // as an ExprStmt. Conjuring a new function and calling it will 797 | // always work. 798 | return &ast.ExprStmt{sb.E.ConjureAndCallFunc(sb.pb.RandType())} 799 | } 800 | 801 | func (sb *StmtBuilder) ClearStmt() *ast.ExprStmt { 802 | 803 | var arg ast.Expr 804 | if rn, ok := sb.S.RandClearable(); ok && sb.R.Intn(3) < 2 { 805 | arg = rn.Name 806 | } else { 807 | if sb.R.Intn(2) == 0 { 808 | arg = sb.E.MakeMakeCall(ArrayOf(sb.pb.RandType())) 809 | } else { 810 | arg = sb.E.MakeMakeCall(MapOf(sb.pb.RandComparableType(), sb.pb.RandType())) 811 | } 812 | } 813 | 814 | return &ast.ExprStmt{ 815 | X: &ast.CallExpr{ 816 | Fun: ClearIdent, 817 | Args: []ast.Expr{arg}, 818 | }, 819 | } 820 | } 821 | 822 | var noName = ast.Ident{Name: "_"} 823 | 824 | // build and return a statement of form 825 | // 826 | // _, _, ... _ = var1, var2, ..., varN 827 | // 828 | // for each i in idents 829 | func (sb *StmtBuilder) UseVars(idents []*ast.Ident) ast.Stmt { 830 | useStmt := &ast.AssignStmt{ 831 | Lhs: make([]ast.Expr, 0, len(idents)), 832 | Tok: token.ASSIGN, 833 | Rhs: make([]ast.Expr, 0, len(idents)), 834 | } 835 | 836 | for _, name := range idents { 837 | useStmt.Lhs = append(useStmt.Lhs, &noName) 838 | useStmt.Rhs = append(useStmt.Rhs, name) 839 | } 840 | return useStmt 841 | } 842 | -------------------------------------------------------------------------------- /microsmith/expr.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "math/rand" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // -------------------------------- 12 | // ExprBuilder Type 13 | // -------------------------------- 14 | 15 | type ExprBuilder struct { 16 | pb *PackageBuilder 17 | C *Context 18 | R *rand.Rand 19 | S *Scope 20 | 21 | depth int // how deep the expr hierarchy is 22 | } 23 | 24 | func NewExprBuilder(pb *PackageBuilder) *ExprBuilder { 25 | return &ExprBuilder{ 26 | pb: pb, 27 | C: pb.ctx, 28 | R: pb.rs, 29 | S: pb.ctx.scope, 30 | } 31 | } 32 | 33 | // -------------------------------- 34 | // Builder Methods 35 | // -------------------------------- 36 | 37 | // Returns true if the expression tree currently being built is 38 | // allowed to become deeper. 39 | func (eb *ExprBuilder) Deepen() bool { 40 | return (eb.depth <= 6) && (eb.R.Float64() < 0.7) 41 | } 42 | 43 | func (eb *ExprBuilder) BasicLit(t BasicType) ast.Expr { 44 | bl := new(ast.BasicLit) 45 | switch t.Name() { 46 | case "byte", "uint32", "uint64", "uint", "uintptr", "int", "int8", "int16", "int32", "int64", "any": 47 | bl.Kind = token.INT 48 | bl.Value = strconv.Itoa(eb.R.Intn(100)) 49 | case "rune": 50 | bl.Kind = token.CHAR 51 | bl.Value = RandRune() 52 | case "float32", "float64": 53 | bl.Kind = token.FLOAT 54 | bl.Value = strconv.FormatFloat(1e4*(eb.R.Float64()), 'f', 1, 64) 55 | case "complex128": 56 | // There's no complex basiclit, generate an IMAG 57 | bl.Kind = token.IMAG 58 | bl.Value = strconv.FormatFloat(1e4*(eb.R.Float64()), 'f', 2, 64) + "i" 59 | case "bool": 60 | if eb.R.Intn(2) == 0 { 61 | return TrueIdent 62 | } else { 63 | return FalseIdent 64 | } 65 | case "string": 66 | bl.Kind = token.STRING 67 | bl.Value = RandString() 68 | default: 69 | panic("Unimplemented for " + t.Name()) 70 | } 71 | 72 | return bl 73 | } 74 | 75 | func (eb *ExprBuilder) CompositeLit(t Type) *ast.CompositeLit { 76 | switch t := t.(type) { 77 | case BasicType: 78 | panic("No CompositeLit of type " + t.Name()) 79 | case ArrayType: 80 | cl := &ast.CompositeLit{Type: t.Ast()} 81 | elems := []ast.Expr{} 82 | if eb.R.Intn(4) > 0 { // plain array literal 83 | for i := 0; i < eb.R.Intn(5); i++ { 84 | if eb.Deepen() { 85 | elems = append(elems, eb.Expr(t.Base())) 86 | } else { 87 | elems = append(elems, eb.VarOrLit(t.Base())) 88 | } 89 | } 90 | } else { // keyed literals 91 | if eb.Deepen() { 92 | elems = append(elems, &ast.KeyValueExpr{ 93 | Key: eb.BasicLit(BT{N: "int"}), 94 | Value: eb.Expr(t.Base()), 95 | }) 96 | } else { 97 | elems = append(elems, &ast.KeyValueExpr{ 98 | Key: eb.BasicLit(BT{N: "int"}), 99 | Value: eb.VarOrLit(t.Base()), 100 | }) 101 | } 102 | } 103 | cl.Elts = elems 104 | return cl 105 | case MapType: 106 | cl := &ast.CompositeLit{Type: t.Ast()} 107 | if eb.Deepen() { 108 | // since Expr as allowed as key, we can generate 109 | // non-constant expr which guarantees no "duplicate key" 110 | // compilation error. 111 | for i := 0; i < eb.R.Intn(4); i++ { 112 | ek, ok := eb.NonConstantExpr(t.KeyT) 113 | cl.Elts = append(cl.Elts, &ast.KeyValueExpr{Key: ek, Value: eb.Expr(t.ValueT)}) 114 | if !ok { 115 | break 116 | } 117 | } 118 | } else { 119 | // No way to guarantee uniqueness, only generate one pair. 120 | cl.Elts = append(cl.Elts, &ast.KeyValueExpr{ 121 | Key: eb.VarOrLit(t.KeyT), 122 | Value: eb.VarOrLit(t.ValueT), 123 | }) 124 | } 125 | return cl 126 | case StructType: 127 | cl := &ast.CompositeLit{Type: t.Ast()} 128 | elems := []ast.Expr{} 129 | for _, t := range t.Ftypes { 130 | if eb.Deepen() { 131 | elems = append(elems, eb.Expr(t)) 132 | } else { 133 | elems = append(elems, eb.VarOrLit(t)) 134 | } 135 | } 136 | cl.Elts = elems 137 | return cl 138 | default: 139 | panic("CompositeLit: unsupported type " + t.Name()) 140 | } 141 | } 142 | 143 | func (eb *ExprBuilder) TypeParamLit(t TypeParam) ast.Expr { 144 | lit := &ast.BasicLit{Kind: token.INT, Value: "77"} 145 | return &ast.CallExpr{ 146 | Fun: t.Ast(), 147 | Args: []ast.Expr{lit}, 148 | } 149 | } 150 | 151 | func (eb *ExprBuilder) Expr(t Type) ast.Expr { 152 | eb.depth++ 153 | defer func() { eb.depth-- }() 154 | 155 | if eb.R.Intn(8) == 0 { 156 | return eb.RandCallExpr(t) 157 | } 158 | 159 | switch t := t.(type) { 160 | 161 | case BasicType, TypeParam: 162 | switch eb.R.Intn(7) { 163 | case 0: 164 | if bt, ok := t.(BasicType); ok { 165 | return eb.Cast(bt) 166 | } 167 | fallthrough 168 | case 1, 2, 3: 169 | return eb.UnaryExpr(t) 170 | case 4, 5, 6: 171 | return eb.BinaryExpr(t) 172 | default: 173 | panic("unreachable") 174 | } 175 | 176 | case ArrayType: 177 | if t.Etype.Name() == "byte" && eb.R.Intn(3) == 0 { 178 | var arg ast.Expr 179 | if eb.Deepen() { 180 | arg = eb.Expr(BT{"string"}) 181 | } else { 182 | arg = eb.VarOrLit(BT{"string"}) 183 | } 184 | return &ast.CallExpr{ 185 | Fun: &ast.Ident{Name: t.Name()}, 186 | Args: []ast.Expr{arg}, 187 | } 188 | } 189 | 190 | if eb.R.Intn(2) == 0 { 191 | return eb.MakeAppendCall(t) 192 | } 193 | return eb.VarOrLit(t) 194 | 195 | case ChanType, FuncType, MapType, StructType: 196 | return eb.VarOrLit(t) 197 | 198 | case InterfaceType: 199 | return CastToType(t, &ast.Ident{Name: "nil"}) 200 | 201 | case PointerType: 202 | // 50/50 between: 203 | // - a new() call 204 | // - a variable of type t, an unary &t.Base(), or a typed nil 205 | if eb.R.Intn(2) == 0 { 206 | ce := &ast.CallExpr{Fun: &ast.Ident{Name: "new"}} 207 | if eb.Deepen() { 208 | ce.Args = []ast.Expr{eb.Expr(t.Base())} 209 | } else { 210 | ce.Args = []ast.Expr{eb.VarOrLit(t.Base())} 211 | } 212 | return ce 213 | } 214 | 215 | vt, typeInScope := eb.S.RandVar(t) 216 | vst, baseInScope := eb.S.RandVar(t.Base()) 217 | switch n := eb.R.Intn(3); { 218 | case n == 0 && typeInScope: 219 | return vt.Name 220 | case n == 1 && baseInScope: 221 | return &ast.UnaryExpr{Op: token.AND, X: vst.Name} 222 | default: 223 | return CastToType(t, &ast.Ident{Name: "nil"}) 224 | } 225 | 226 | default: 227 | panic("Unimplemented type " + t.Name()) 228 | } 229 | } 230 | 231 | // NonConstantExpr is like Expr(), except it either returns a non 232 | // constant expression (and true), or signals failure to do so by 233 | // returning false as the second value. 234 | func (eb *ExprBuilder) NonConstantExpr(t Type) (ast.Expr, bool) { 235 | eb.depth++ 236 | defer func() { eb.depth-- }() 237 | 238 | switch t := t.(type) { 239 | case BasicType: 240 | v, ok := eb.S.RandVar(t) 241 | if !ok { 242 | return eb.Expr(t), false 243 | } 244 | op := RandItem(eb.R, BinOps(t)) 245 | // Reject shifts to avoid "negative shift count" errors. In 246 | // other places we handle the issue by forcing the RHS to be 247 | // an uint, but here we don't want to change the operand's 248 | // types. Also reject divisions to avoid "division by zero" 249 | // errors. 250 | if op == token.SHL || op == token.SHR || op == token.REM || op == token.QUO { 251 | op = token.ADD 252 | } 253 | return &ast.BinaryExpr{ 254 | X: v.Name, 255 | Op: op, 256 | Y: eb.Expr(t), 257 | }, true 258 | default: 259 | return eb.Expr(t), true 260 | } 261 | } 262 | 263 | func (eb *ExprBuilder) VarOrLit(t Type) ast.Expr { 264 | // If t is a type parameter, 50/50 between returning a variable 265 | // and building a literal; except for type parameters that don't 266 | // allow literals (like interface { int | []int }); for those it's 267 | // always a variable. 268 | if tp, ok := t.(TypeParam); ok { 269 | if tp.HasLiterals() && eb.R.Intn(2) == 0 { 270 | return eb.TypeParamLit(tp) 271 | } 272 | if v, ok := eb.S.RandVar(t); !ok { 273 | panic("VarOrLit couldn't find a type parameter variable") 274 | } else { 275 | return v.Name 276 | } 277 | } 278 | 279 | vst, typeCanDerive := eb.S.RandVarSubType(t) 280 | if !typeCanDerive || !eb.Deepen() { 281 | switch t := t.(type) { 282 | case BasicType: 283 | bl := eb.BasicLit(t) 284 | if t.NeedsCast() { 285 | bl = CastToType(t, bl) 286 | } 287 | return bl 288 | case ArrayType, MapType: 289 | if eb.R.Intn(3) == 0 { 290 | return eb.MakeMakeCall(t) 291 | } else { 292 | return eb.CompositeLit(t) 293 | } 294 | case StructType: 295 | return eb.CompositeLit(t) 296 | case ChanType: 297 | // No literal of type Chan, but we can return make(chan t) 298 | return &ast.CallExpr{ 299 | Fun: &ast.Ident{Name: "make"}, 300 | Args: []ast.Expr{ 301 | &ast.ChanType{Dir: 3, Value: t.Base().Ast()}, 302 | }, 303 | } 304 | case PointerType, FuncType, InterfaceType: 305 | return CastToType(t, &ast.Ident{Name: "nil"}) 306 | case TypeParam: 307 | return eb.TypeParamLit(t) 308 | default: 309 | panic("unhandled type " + t.Name()) 310 | } 311 | } 312 | 313 | // Slice, once in a while. 314 | if _, ok := vst.Type.(ArrayType); ok { 315 | if t.Equal(vst.Type) && eb.R.Intn(4) == 0 { 316 | return eb.SliceExpr(vst) 317 | } 318 | } 319 | if bt, ok := vst.Type.(BasicType); ok && bt.N == "string" { 320 | if t.Equal(vst.Type) && eb.R.Intn(4) == 0 { 321 | return eb.SliceExpr(vst) 322 | } 323 | } 324 | 325 | return eb.SubTypeExpr(vst.Name, vst.Type, t) 326 | } 327 | 328 | // e: the Expr being built 329 | // t: the current type of e 330 | // target: the target type of e 331 | func (eb *ExprBuilder) SubTypeExpr(e ast.Expr, t, target Type) ast.Expr { 332 | if t.Equal(target) { 333 | return e 334 | } 335 | 336 | eb.depth++ 337 | defer func() { eb.depth-- }() 338 | 339 | switch t := t.(type) { 340 | case ArrayType: 341 | return eb.SubTypeExpr(eb.IndexExpr(e), t.Base(), target) 342 | case BasicType: 343 | panic("basic types should not get here") 344 | case ChanType: 345 | return eb.SubTypeExpr(eb.ChanReceiveExpr(e), t.Base(), target) 346 | case MapType: 347 | return eb.SubTypeExpr(eb.MapIndexExpr(e, t.KeyT), t.ValueT, target) 348 | case PointerType: 349 | return eb.SubTypeExpr(eb.StarExpr(e), t.Base(), target) 350 | case StructType: 351 | return eb.SubTypeExpr(eb.StructFieldExpr(e, t, target), target, target) 352 | case FuncType: 353 | return eb.SubTypeExpr(eb.CallExpr(e, t.Args), t.Ret[0], target) 354 | case InterfaceType: 355 | return eb.SubTypeExpr(eb.MethodExpr(e, t, target), target, target) 356 | default: 357 | panic("unhandled type " + t.Name()) 358 | } 359 | } 360 | 361 | // Returns e(...) 362 | func (eb *ExprBuilder) CallExpr(e ast.Expr, at []Type) *ast.CallExpr { 363 | // Sometimes e is not a normal function, but one needing special 364 | // handling of its arguments (builtins like len, or func from the 365 | // unsafe package). If that's the case, delegate to CallFunction. 366 | if ident, ok := e.(*ast.Ident); ok && !(strings.HasPrefix(ident.Name, "fnc") || strings.HasPrefix(ident.Name, "p")) { 367 | // Must be a builtin or stdlib func. Find corresponding 368 | // Variable in Scope, and build a call. 369 | if v, ok := eb.S.FindVarByName(ident.Name); ok { 370 | _, ok := v.Type.(FuncType) 371 | if ok { 372 | return eb.CallFunction(v) 373 | } 374 | } else { 375 | panic("unreachable") 376 | } 377 | } 378 | 379 | var args []ast.Expr 380 | for _, a := range at { 381 | t := a 382 | if arg, ok := (a).(EllipsisType); ok { 383 | t = arg.Base 384 | } 385 | if eb.R.Intn(2) == 0 && eb.Deepen() { 386 | args = append(args, eb.Expr(t)) 387 | } else { 388 | args = append(args, eb.VarOrLit(t)) 389 | } 390 | } 391 | return &ast.CallExpr{Fun: e, Args: args} 392 | } 393 | 394 | // Returns v.M(...) 395 | func (eb *ExprBuilder) MethodExpr(e ast.Expr, t InterfaceType, target Type) ast.Expr { 396 | for _, m := range t.Methods { 397 | if m.Func.Contains(target) { 398 | sl := &ast.SelectorExpr{X: e, Sel: m.Name} 399 | if m.Func.Equal(target) { 400 | return sl 401 | } else { 402 | return eb.CallExpr(sl, m.Func.Args) 403 | } 404 | } 405 | } 406 | 407 | panic("unreachable:" + t.Name() + " " + " target: " + target.Name()) 408 | } 409 | 410 | // Returns e[...] 411 | func (eb *ExprBuilder) IndexExpr(e ast.Expr) *ast.IndexExpr { 412 | var i ast.Expr 413 | if eb.R.Intn(2) == 0 && eb.Deepen() { 414 | i, _ = eb.NonConstantExpr(BT{"int"}) 415 | } else { 416 | i = eb.VarOrLit(BT{"int"}) 417 | } 418 | 419 | return &ast.IndexExpr{X: e, Index: i} 420 | } 421 | 422 | // Returns *e 423 | func (eb *ExprBuilder) StarExpr(e ast.Expr) *ast.UnaryExpr { 424 | return &ast.UnaryExpr{Op: token.MUL, X: e} 425 | } 426 | 427 | // Returns e[k] with e map and k of type t 428 | func (eb *ExprBuilder) MapIndexExpr(e ast.Expr, t Type) *ast.IndexExpr { 429 | var i ast.Expr 430 | if eb.Deepen() { 431 | i = eb.Expr(t) 432 | } else { 433 | i = eb.VarOrLit(t) 434 | } 435 | 436 | return &ast.IndexExpr{X: e, Index: i} 437 | } 438 | 439 | // Returns e.<...> where the whole expression has type target. 440 | func (eb *ExprBuilder) StructFieldExpr(e ast.Expr, t StructType, target Type) ast.Expr { 441 | for i, ft := range t.Ftypes { 442 | if ft.Contains(target) { 443 | sl := &ast.SelectorExpr{ 444 | X: e, 445 | Sel: &ast.Ident{Name: t.Fnames[i]}, 446 | } 447 | return eb.SubTypeExpr(sl, ft, target) 448 | } 449 | } 450 | panic("unreachable:" + t.Name() + " " + " target: " + target.Name()) 451 | } 452 | 453 | // Returns <-e 454 | func (eb *ExprBuilder) ChanReceiveExpr(e ast.Expr) *ast.UnaryExpr { 455 | return &ast.UnaryExpr{Op: token.ARROW, X: e} 456 | } 457 | 458 | func (eb *ExprBuilder) SliceExpr(v Variable) *ast.SliceExpr { 459 | if !v.Type.Sliceable() { 460 | panic("Cannot slice type " + v.Type.Name()) 461 | } 462 | 463 | var low, high ast.Expr 464 | var ok bool 465 | if eb.Deepen() { 466 | if eb.R.Intn(8) > 0 { 467 | low, ok = eb.NonConstantExpr(BT{"int"}) 468 | if !ok { 469 | panic("NonConstantExpr of int failed") 470 | } 471 | } 472 | if eb.R.Intn(8) > 0 { 473 | high, ok = eb.NonConstantExpr(BT{"int"}) 474 | if !ok { 475 | panic("NonConstantExpr of int failed") 476 | } 477 | } 478 | } else { 479 | if eb.R.Intn(8) > 0 { 480 | low = &ast.BasicLit{ 481 | Kind: token.INT, 482 | Value: strconv.Itoa(eb.R.Intn(8)), 483 | } 484 | } 485 | if eb.R.Intn(8) > 0 { 486 | high = &ast.BasicLit{ 487 | Kind: token.INT, 488 | Value: strconv.Itoa(8 + eb.R.Intn(17)), 489 | } 490 | } 491 | } 492 | 493 | return &ast.SliceExpr{ 494 | X: v.Name, 495 | Low: low, 496 | High: high, 497 | } 498 | } 499 | 500 | // returns an *ast.UnaryExpr of type t, or a VarOrLit as fallback if 501 | // type t has no unary operators. 502 | func (eb *ExprBuilder) UnaryExpr(t Type) ast.Expr { 503 | ue := new(ast.UnaryExpr) 504 | 505 | // if there are pointers to t in scope, generate a t by 506 | // dereferencing it with chance 0.5 507 | if eb.R.Intn(2) == 0 && eb.S.Has(PointerOf(t)) { 508 | ue.Op = token.MUL 509 | // See comment in Expr() for PointerType on why we must call 510 | // Expr() here. 511 | ue.X = eb.Expr(PointerOf(t)) 512 | return ue 513 | } 514 | 515 | if ops := UnaryOps(t); len(ops) > 0 { 516 | ue.Op = RandItem(eb.R, ops) 517 | } else { 518 | return eb.VarOrLit(t) 519 | } 520 | 521 | if eb.Deepen() { 522 | ue.X = eb.Expr(t) 523 | } else { 524 | ue.X = eb.VarOrLit(t) 525 | } 526 | 527 | return ue 528 | } 529 | 530 | func (eb *ExprBuilder) BinaryExpr(t Type) ast.Expr { 531 | ue := new(ast.BinaryExpr) 532 | 533 | ops := BinOps(t) 534 | if t.Name() == "bool" && eb.R.Intn(2) == 0 { 535 | // for booleans, we 50/50 between BOOL_OP and 536 | // COMPARISON . 537 | t = eb.pb.RandComparableType() 538 | ops = []token.Token{token.EQL, token.NEQ} 539 | if IsOrdered(t) { 540 | ops = append(ops, []token.Token{ 541 | token.LSS, token.LEQ, 542 | token.GTR, token.GEQ}...) 543 | } 544 | } 545 | if len(ops) > 0 { 546 | ue.Op = RandItem(eb.R, ops) 547 | } else { 548 | return eb.VarOrLit(t) 549 | } 550 | 551 | t2 := t 552 | if ue.Op == token.SHR { // ensure rhs > 0 for shifts 553 | t2 = BT{"uint"} 554 | } 555 | 556 | // For some types, we need to ensure at least one leaf of the expr 557 | // tree is a variable, or we'll trigger compilation errors as 558 | // "constant overflows uint" on Exprs that end up being all 559 | // literals (and thus computable at compile time), and outside the 560 | // type's range. 561 | if _, isTP := t.(TypeParam); IsNumeric(t) || isTP { 562 | 563 | // LHS can be whatever 564 | if eb.Deepen() { 565 | ue.X = eb.Expr(t) 566 | } else { 567 | ue.X = eb.VarOrLit(t) 568 | } 569 | 570 | // Make sure the RHS is not a constant expression. The result 571 | // of len, min, and max are const when their args are consts, 572 | // so we need to avoid them. 573 | if vi, ok := eb.S.RandVarSubType(t2); ok && (vi.Name.Name != "len" && vi.Name.Name != "min" && vi.Name.Name != "max") { 574 | // If we can use some existing variable, do that. 575 | ue.Y = eb.SubTypeExpr(vi.Name, vi.Type, t2) 576 | } else { 577 | // Otherwise, cast from an int. 578 | vi, _ := eb.S.RandVar(BT{"int"}) 579 | ue.Y = CastToType(t2, vi.Name) 580 | } 581 | 582 | return ue 583 | } 584 | 585 | if eb.Deepen() { 586 | ue.X = eb.Expr(t) 587 | if ue.Op != token.SHR { 588 | ue.Y = eb.Expr(t2) 589 | } else { 590 | // The compiler rejects stupid shifts, so we need control 591 | // on the shift amount. 592 | ue.Y = eb.VarOrLit(t2) 593 | } 594 | } else { 595 | ue.X = eb.VarOrLit(t) 596 | ue.Y = eb.VarOrLit(t2) 597 | } 598 | 599 | return ue 600 | } 601 | 602 | func (eb *ExprBuilder) Cast(t BasicType) *ast.CallExpr { 603 | 604 | // handle string([]byte) cast 605 | if t.Equal(BT{"string"}) { 606 | var arg ast.Expr 607 | if eb.Deepen() { 608 | arg = eb.Expr(ArrayOf(BT{"byte"})) 609 | } else { 610 | arg = eb.VarOrLit(ArrayOf(BT{"byte"})) 611 | } 612 | return &ast.CallExpr{ 613 | Fun: &ast.Ident{Name: t.N}, 614 | Args: []ast.Expr{arg}, 615 | } 616 | } 617 | 618 | // numeric casts 619 | t2 := t 620 | if IsNumeric(t) { 621 | t2 = eb.pb.RandNumericType() 622 | for strings.HasPrefix(t2.N, "float") { 623 | t2 = eb.pb.RandNumericType() 624 | } 625 | } 626 | 627 | return &ast.CallExpr{ 628 | Fun: &ast.Ident{Name: t.N}, 629 | Args: []ast.Expr{eb.VarOrLit(t2)}, 630 | } 631 | } 632 | 633 | // CallExpr returns a call expression with return value of type t. The 634 | // function can be a builtin or stdlib function, a locally defined 635 | // function variable, or a function literal that is immediately 636 | // called. 637 | func (eb *ExprBuilder) RandCallExpr(t Type) *ast.CallExpr { 638 | if v, ok := eb.S.RandFuncRet(t); ok && !eb.C.inDefer && eb.R.Intn(4) > 0 { 639 | return eb.CallFunction(v, t) 640 | } else { 641 | return eb.ConjureAndCallFunc(t) 642 | } 643 | } 644 | 645 | // MakeCall builds an ast.CallExpr calling the function in variable v, 646 | // taking care of setting up its arguments, including for functions 647 | // like copy() or unsafe.Alignof that require custom handling. 648 | func (eb *ExprBuilder) CallFunction(v Variable, ct ...Type) *ast.CallExpr { 649 | f, ok := v.Type.(FuncType) 650 | if !ok { 651 | panic("CallFunction: not a function: " + v.Name.Name) 652 | } 653 | 654 | name := v.Name.Name 655 | ce := &ast.CallExpr{} 656 | if i := strings.Index(name, "."); i >= 0 { 657 | ce.Fun = &ast.SelectorExpr{ 658 | X: &ast.Ident{Name: name[:i]}, 659 | Sel: &ast.Ident{Name: name[i+1:]}, 660 | } 661 | } else { 662 | ce.Fun = v.Name 663 | } 664 | 665 | switch name { 666 | 667 | case "copy": 668 | var t1, t2 Type 669 | if eb.R.Intn(3) == 0 { 670 | t1, t2 = ArrayOf(BT{N: "byte"}), BT{N: "string"} 671 | } else { 672 | t1 = ArrayOf(eb.pb.RandType()) 673 | t2 = t1 674 | } 675 | if eb.Deepen() { 676 | ce.Args = []ast.Expr{eb.Expr(t1), eb.Expr(t2)} 677 | } else { 678 | ce.Args = []ast.Expr{eb.VarOrLit(t1), eb.VarOrLit(t2)} 679 | } 680 | 681 | case "len": 682 | var t Type 683 | if eb.R.Intn(3) < 2 { 684 | t = ArrayOf(eb.pb.RandType()) 685 | } else { 686 | t = BT{"string"} 687 | } 688 | if eb.Deepen() { 689 | ce.Args = []ast.Expr{eb.Expr(t)} 690 | } else { 691 | ce.Args = []ast.Expr{eb.VarOrLit(t)} 692 | } 693 | 694 | case "min", "max": 695 | if len(ct) == 0 { 696 | panic("min/max need additional type arg") 697 | } 698 | t := ct[0] 699 | 700 | for i := 0; i < 1+eb.R.Intn(5); i++ { 701 | if eb.Deepen() { 702 | ce.Args = append(ce.Args, eb.Expr(t)) 703 | } else { 704 | ce.Args = append(ce.Args, eb.VarOrLit(t)) 705 | } 706 | } 707 | 708 | case "unsafe.Offsetof": 709 | var sl *ast.SelectorExpr 710 | 711 | // If we can get a variable, use that (as long it has at least 712 | // one field). Otherwise, conjure a literal of a random struct 713 | // type. 714 | v, ok := eb.S.RandStruct() 715 | if ok && len(v.Type.(StructType).Fnames) > 0 { 716 | sl = &ast.SelectorExpr{ 717 | X: v.Name, 718 | Sel: &ast.Ident{Name: RandItem(eb.R, v.Type.(StructType).Fnames)}, 719 | } 720 | } else { 721 | var st StructType 722 | for len(st.Fnames) == 0 { 723 | st = eb.pb.RandStructType() 724 | } 725 | sl = &ast.SelectorExpr{ 726 | X: eb.VarOrLit(st), 727 | Sel: &ast.Ident{Name: RandItem(eb.R, st.Fnames)}, 728 | } 729 | } 730 | ce.Args = []ast.Expr{sl} 731 | 732 | case "unsafe.Sizeof", "unsafe.Alignof": 733 | t := eb.pb.RandType() 734 | _, isPtr := t.(PointerType) 735 | _, isFnc := t.(FuncType) 736 | _, isInt := t.(InterfaceType) 737 | for isPtr || isFnc || isInt { 738 | t = eb.pb.RandType() 739 | _, isPtr = t.(PointerType) 740 | _, isFnc = t.(FuncType) 741 | _, isInt = t.(InterfaceType) 742 | } 743 | if eb.Deepen() { 744 | ce.Args = []ast.Expr{eb.Expr(t)} 745 | } else { 746 | ce.Args = []ast.Expr{eb.VarOrLit(t)} 747 | } 748 | 749 | case "unsafe.SliceData": 750 | if len(ct) == 0 { 751 | panic("unsafe.SliceData needs additional type arg") 752 | } 753 | t := ArrayOf(ct[0].(PointerType).Base()) 754 | if eb.Deepen() { 755 | ce.Args = []ast.Expr{eb.Expr(t)} 756 | } else { 757 | ce.Args = []ast.Expr{eb.VarOrLit(t)} 758 | } 759 | 760 | case "reflect.DeepEqual": 761 | t1, t2 := eb.pb.RandType(), eb.pb.RandType() 762 | if eb.Deepen() { 763 | ce.Args = []ast.Expr{eb.Expr(t1), eb.Expr(t2)} 764 | } else { 765 | ce.Args = []ast.Expr{eb.VarOrLit(t1), eb.VarOrLit(t2)} 766 | } 767 | 768 | case "slices.All": 769 | if len(ct) == 0 { 770 | panic("slices.All needs additional type arg") 771 | } 772 | t := ArrayOf(ct[0]) 773 | if eb.Deepen() { 774 | ce.Args = []ast.Expr{eb.Expr(t)} 775 | } else { 776 | ce.Args = []ast.Expr{eb.VarOrLit(t)} 777 | } 778 | 779 | default: 780 | if f.Args == nil || f.Ret == nil { 781 | panic("CallFunction: missing special handling for " + name) 782 | } 783 | 784 | args := make([]ast.Expr, 0, len(f.Args)) 785 | for _, arg := range f.Args { 786 | arg := arg 787 | if ep, ok := arg.(EllipsisType); ok { 788 | arg = ep.Base 789 | } 790 | if eb.Deepen() && f.Local { 791 | // Cannot call Expr with casts, because UnaryExpr 792 | // could return e.g. -11 which cannot be cast to uint. 793 | args = append(args, eb.Expr(arg)) 794 | } else { 795 | args = append(args, eb.VarOrLit(arg)) 796 | } 797 | } 798 | ce.Args = args 799 | } 800 | 801 | return ce 802 | 803 | } 804 | 805 | func (eb *ExprBuilder) MakeAppendCall(t ArrayType) *ast.CallExpr { 806 | ce := &ast.CallExpr{Fun: AppendIdent} 807 | 808 | t2 := t.Base() 809 | ellips := token.Pos(0) 810 | if eb.R.Intn(3) == 0 { // 2nd arg is ... 811 | t2 = t 812 | ellips = token.Pos(1) 813 | } 814 | 815 | if eb.Deepen() { 816 | ce.Args = []ast.Expr{eb.Expr(t), eb.Expr(t2)} 817 | } else { 818 | ce.Args = []ast.Expr{eb.VarOrLit(t), eb.VarOrLit(t2)} 819 | } 820 | ce.Ellipsis = ellips 821 | 822 | return ce 823 | } 824 | 825 | func (eb *ExprBuilder) MakeMakeCall(t Type) *ast.CallExpr { 826 | 827 | ce := &ast.CallExpr{Fun: MakeIdent} 828 | 829 | switch t := t.(type) { 830 | case ArrayType: 831 | tn := t.Base().Ast() 832 | if eb.Deepen() { 833 | ce.Args = []ast.Expr{&ast.ArrayType{Elt: tn}, eb.BinaryExpr(BT{"int"})} 834 | } else { 835 | ce.Args = []ast.Expr{&ast.ArrayType{Elt: tn}, eb.VarOrLit(BT{"int"})} 836 | } 837 | case MapType: 838 | tk, tv := t.KeyT.Ast(), t.ValueT.Ast() 839 | if eb.Deepen() { 840 | ce.Args = []ast.Expr{&ast.MapType{Key: tk, Value: tv}, eb.BinaryExpr(BT{"int"})} 841 | } else { 842 | ce.Args = []ast.Expr{&ast.MapType{Key: tk, Value: tv}, eb.VarOrLit(BT{"int"})} 843 | } 844 | default: 845 | panic("MakeMakeCall: invalid type " + t.Name()) 846 | } 847 | 848 | return ce 849 | } 850 | 851 | func (eb *ExprBuilder) ConjureAndCallFunc(t Type) *ast.CallExpr { 852 | 853 | ft := &FuncType{"FU", []Type{}, []Type{t}, true} 854 | for i := 0; i < eb.R.Intn(5); i++ { 855 | ft.Args = append(ft.Args, eb.pb.RandType()) 856 | } 857 | 858 | var retExpr ast.Expr 859 | if eb.Deepen() { 860 | retExpr = eb.Expr(t) 861 | } else { 862 | retExpr = eb.VarOrLit(t) 863 | } 864 | 865 | p, r := ft.MakeFieldLists(false, 0) 866 | fl := &ast.FuncLit{ 867 | Type: &ast.FuncType{Params: p, Results: r}, 868 | Body: &ast.BlockStmt{List: []ast.Stmt{ 869 | eb.pb.sb.AssignStmt(), // one statement 870 | &ast.ReturnStmt{Results: []ast.Expr{retExpr}}, // the return 871 | }}, 872 | } 873 | 874 | // If we are in a defer, optionally add a recover call before 875 | // the return statement. 876 | if eb.C.inDefer && eb.R.Intn(4) == 0 { 877 | fl.Body.List = append( 878 | []ast.Stmt{&ast.ExprStmt{&ast.CallExpr{Fun: &ast.Ident{Name: "recover"}}}}, 879 | fl.Body.List..., 880 | ) 881 | } 882 | 883 | // Finally, call it. 884 | args := make([]ast.Expr, 0, len(ft.Args)) 885 | for _, arg := range ft.Args { 886 | args = append(args, eb.VarOrLit(arg)) 887 | } 888 | return &ast.CallExpr{Fun: fl, Args: args} 889 | } 890 | -------------------------------------------------------------------------------- /microsmith/types.go: -------------------------------------------------------------------------------- 1 | package microsmith 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/ast" 7 | "go/format" 8 | "go/token" 9 | "strings" 10 | ) 11 | 12 | type Type interface { 13 | Comparable() bool // in the go specification sense 14 | Ast() ast.Expr // suitable for type declaration in the ast 15 | Equal(t Type) bool // is t of the same type 16 | Name() string // human-readable type name 17 | Sliceable() bool // can [] 18 | Contains(t Type) bool // does the type contain type t 19 | } 20 | 21 | // Name to use for variables of type t 22 | func Ident(t Type) string { 23 | switch t := t.(type) { 24 | case BasicType: 25 | switch t.N { 26 | case "bool": 27 | return "b" 28 | case "byte": 29 | return "by" 30 | case "int8": 31 | return "i8_" 32 | case "int16": 33 | return "i16_" 34 | case "int32": 35 | return "i32_" 36 | case "int64": 37 | return "i64_" 38 | case "int": 39 | return "i" 40 | case "uint32": 41 | return "u32_" 42 | case "uint64": 43 | return "u64_" 44 | case "uint": 45 | return "u" 46 | case "uintptr": 47 | return "up" 48 | case "float32": 49 | return "h" 50 | case "float64": 51 | return "f" 52 | case "complex128": 53 | return "c" 54 | case "string": 55 | return "s" 56 | case "rune": 57 | return "r" 58 | case "any": 59 | return "an" 60 | default: 61 | panic("unhandled type " + t.N) 62 | } 63 | case ArrayType: 64 | return "a" + Ident(t.Etype) 65 | case FuncType: 66 | if t.N != "FU" { 67 | // buildin and stdlib functions don't need identifiers 68 | return "" 69 | } else { 70 | return "fnc" 71 | } 72 | case StructType: 73 | return "st" 74 | case ChanType: 75 | return "ch" 76 | case MapType: 77 | return "m" 78 | case PointerType: 79 | return "p" + Ident(t.Btype) 80 | case TypeParam: 81 | return strings.ToLower(t.N.Name) + "_" 82 | case InterfaceType: 83 | return "in" 84 | default: 85 | panic("Ident: unknown type " + t.Name()) 86 | } 87 | } 88 | 89 | // -------------------------------- 90 | // basic 91 | // -------------------------------- 92 | 93 | type BasicType struct { 94 | N string 95 | } 96 | 97 | func (t BasicType) Comparable() bool { 98 | return t.N != "any" 99 | } 100 | 101 | func (t BasicType) Ast() ast.Expr { 102 | return TypeIdent(t.N) 103 | } 104 | 105 | func (t BasicType) Contains(t2 Type) bool { 106 | return t.Equal(t2) 107 | } 108 | 109 | func (t BasicType) Equal(t2 Type) bool { 110 | if t2, ok := t2.(BasicType); !ok { 111 | return false 112 | } else { 113 | return t.N == t2.N 114 | } 115 | } 116 | 117 | func (bt BasicType) Name() string { 118 | return bt.N 119 | } 120 | 121 | func (bt BasicType) Sliceable() bool { 122 | return bt.N == "string" 123 | } 124 | 125 | func IsNumeric(t Type) bool { 126 | if bt, ok := t.(BasicType); !ok { 127 | return false 128 | } else { 129 | switch bt.N { 130 | case "int", "int8", "int16", "int32", "int64": 131 | return true 132 | case "byte", "uint", "uint8", "uint16", "uint32", "uint64": 133 | return true 134 | case "float32", "float64": 135 | return true 136 | default: 137 | return false 138 | } 139 | } 140 | } 141 | 142 | func IsOrdered(t Type) bool { 143 | if bt, ok := t.(BasicType); !ok { 144 | return false 145 | } else { 146 | switch bt.Name() { 147 | case "bool", "complex128": 148 | return false 149 | default: 150 | return true 151 | } 152 | } 153 | } 154 | 155 | func (t BasicType) NeedsCast() bool { 156 | switch t.N { 157 | case "byte", "int8", "int16", "int32", "int64", "uint", "uint32", "uint64", "uintptr", "float32", "any": 158 | return true 159 | default: 160 | return false 161 | } 162 | } 163 | 164 | // -------------------------------- 165 | // pointer 166 | // -------------------------------- 167 | 168 | type PointerType struct { 169 | Btype Type 170 | } 171 | 172 | func (t PointerType) Comparable() bool { 173 | return true 174 | } 175 | 176 | func (t PointerType) Ast() ast.Expr { 177 | return &ast.StarExpr{X: t.Base().Ast()} 178 | } 179 | 180 | func (t PointerType) Base() Type { 181 | return t.Btype 182 | } 183 | 184 | func (t PointerType) Contains(t2 Type) bool { 185 | if t.Equal(t2) { 186 | return true 187 | } else { 188 | return t.Base().Contains(t2) 189 | } 190 | } 191 | 192 | func (pt PointerType) Equal(t Type) bool { 193 | if t2, ok := t.(PointerType); !ok { 194 | return false 195 | } else { 196 | return pt.Base().Equal(t2.Base()) 197 | } 198 | } 199 | 200 | func (pt PointerType) Name() string { 201 | return "*" + pt.Btype.Name() 202 | } 203 | 204 | func (pt PointerType) Sliceable() bool { 205 | return false 206 | } 207 | 208 | func PointerOf(t Type) PointerType { 209 | return PointerType{t} 210 | } 211 | 212 | // -------------------------------- 213 | // array 214 | // -------------------------------- 215 | 216 | type ArrayType struct { 217 | Etype Type 218 | } 219 | 220 | func (t ArrayType) Comparable() bool { 221 | return false 222 | } 223 | 224 | func (t ArrayType) Ast() ast.Expr { 225 | return &ast.ArrayType{Elt: t.Base().Ast()} 226 | } 227 | 228 | func (at ArrayType) Base() Type { 229 | return at.Etype 230 | } 231 | 232 | func (t ArrayType) Contains(t2 Type) bool { 233 | if t.Equal(t2) { 234 | return true 235 | } else { 236 | return t.Base().Contains(t2) 237 | } 238 | } 239 | 240 | func (at ArrayType) Equal(t Type) bool { 241 | if t2, ok := t.(ArrayType); !ok { 242 | return false 243 | } else { 244 | return at.Base().Equal(t2.Base()) 245 | } 246 | } 247 | 248 | func (at ArrayType) Name() string { 249 | return "[]" + at.Etype.Name() 250 | } 251 | 252 | func (at ArrayType) Sliceable() bool { 253 | return true 254 | } 255 | 256 | func ArrayOf(t Type) ArrayType { 257 | return ArrayType{t} 258 | } 259 | 260 | // -------------------------------- 261 | // struct 262 | // -------------------------------- 263 | 264 | type StructType struct { 265 | Ftypes []Type // fields types 266 | Fnames []string // field names 267 | } 268 | 269 | func (t StructType) Comparable() bool { 270 | for _, t := range t.Ftypes { 271 | if !t.Comparable() { 272 | return false 273 | } 274 | } 275 | return true 276 | } 277 | 278 | func (t StructType) Ast() ast.Expr { 279 | fields := make([]*ast.Field, 0, len(t.Fnames)) 280 | for i := range t.Fnames { 281 | field := &ast.Field{ 282 | Names: []*ast.Ident{&ast.Ident{Name: t.Fnames[i]}}, 283 | Type: t.Ftypes[i].Ast(), 284 | } 285 | fields = append(fields, field) 286 | } 287 | 288 | return &ast.StructType{ 289 | Fields: &ast.FieldList{List: fields}, 290 | } 291 | } 292 | 293 | func (t StructType) Contains(t2 Type) bool { 294 | if t.Equal(t2) { 295 | return true 296 | } 297 | 298 | for _, ft := range t.Ftypes { 299 | if ft.Contains(t2) { 300 | return true 301 | } 302 | } 303 | return false 304 | } 305 | 306 | func (st StructType) Equal(t Type) bool { 307 | if t2, ok := t.(StructType); !ok { 308 | return false 309 | } else { 310 | if len(st.Ftypes) != len(t2.Ftypes) { 311 | return false 312 | } 313 | for i := range st.Ftypes { 314 | if !st.Ftypes[i].Equal(t2.Ftypes[i]) { 315 | return false 316 | } 317 | } 318 | } 319 | return true 320 | } 321 | 322 | func (st StructType) Name() string { 323 | var buf bytes.Buffer 324 | format.Node(&buf, token.NewFileSet(), st.Ast()) 325 | return buf.String() 326 | } 327 | 328 | func (st StructType) Sliceable() bool { 329 | return false 330 | } 331 | 332 | // -------------------------------- 333 | // func 334 | // -------------------------------- 335 | 336 | type FuncType struct { 337 | N string 338 | Args []Type 339 | Ret []Type 340 | Local bool 341 | } 342 | 343 | func (t FuncType) Comparable() bool { 344 | return false 345 | } 346 | 347 | func (t FuncType) Ast() ast.Expr { 348 | p, r := t.MakeFieldLists(false, 0) 349 | return &ast.FuncType{Params: p, Results: r} 350 | } 351 | 352 | func (ft FuncType) Equal(t Type) bool { 353 | if t, ok := t.(FuncType); !ok { 354 | return false 355 | } else { 356 | if ft.Local != t.Local { 357 | return false 358 | } 359 | 360 | if len(ft.Ret) != len(t.Ret) { 361 | return false 362 | } 363 | for i := range ft.Ret { 364 | if !ft.Ret[i].Equal(t.Ret[i]) { 365 | return false 366 | } 367 | } 368 | 369 | if len(ft.Args) != len(t.Args) { 370 | return false 371 | } 372 | for i := range ft.Args { 373 | if !ft.Args[i].Equal(t.Args[i]) { 374 | return false 375 | } 376 | } 377 | return true 378 | } 379 | } 380 | 381 | func (f FuncType) Contains(t Type) bool { 382 | if f.Equal(t) { 383 | return true 384 | } 385 | return len(f.Ret) > 0 && t.Equal(f.Ret[0]) 386 | } 387 | 388 | func (ft FuncType) Name() string { 389 | var buf bytes.Buffer 390 | format.Node(&buf, token.NewFileSet(), ft.Ast()) 391 | return buf.String() 392 | } 393 | 394 | func (ft FuncType) Sliceable() bool { 395 | return false 396 | } 397 | 398 | // Internal implementation detail for variadic parameters in 399 | // functions. Should never be generated or be part of a scope. 400 | type EllipsisType struct { 401 | Base Type 402 | } 403 | 404 | func (e EllipsisType) Ast() ast.Expr { 405 | return &ast.Ellipsis{Elt: e.Base.Ast()} 406 | } 407 | 408 | func (e EllipsisType) Equal(t Type) bool { 409 | if t, ok := t.(EllipsisType); !ok { 410 | return false 411 | } else { 412 | return e.Base.Equal(t.Base) 413 | } 414 | } 415 | 416 | func (e EllipsisType) Comparable() bool { panic("don't call") } 417 | func (e EllipsisType) Name() string { panic("don't call") } 418 | func (e EllipsisType) Sliceable() bool { panic("don't call") } 419 | func (e EllipsisType) Contains(t Type) bool { panic("don't call") } 420 | 421 | // Build two ast.FieldList object (one for params, the other for 422 | // results) from a FuncType, to use in function declarations and 423 | // function literals. If named is true, it gives the function 424 | // parameters names (p, p, ...) 425 | func (ft FuncType) MakeFieldLists(named bool, s int) (*ast.FieldList, *ast.FieldList) { 426 | params := &ast.FieldList{ 427 | List: make([]*ast.Field, 0, len(ft.Args)), 428 | } 429 | for i, arg := range ft.Args { 430 | p := ast.Field{Type: arg.Ast()} 431 | if named { 432 | p.Names = []*ast.Ident{ 433 | &ast.Ident{Name: fmt.Sprintf("p%d", s+i)}, 434 | } 435 | } 436 | params.List = append(params.List, &p) 437 | } 438 | 439 | results := &ast.FieldList{ 440 | List: make([]*ast.Field, 0, len(ft.Ret)), 441 | } 442 | for _, arg := range ft.Ret { 443 | results.List = append( 444 | results.List, 445 | &ast.Field{Type: arg.Ast()}, 446 | ) 447 | } 448 | 449 | return params, results 450 | } 451 | 452 | type BT = BasicType 453 | 454 | // builtins and a few standard library functions to generate calls to. 455 | // Any null Args or Ret is custom-handled when building the function 456 | // call. 457 | 458 | var BuiltinsFuncs = []FuncType{ 459 | { 460 | N: "append", 461 | Args: nil, 462 | Ret: nil, 463 | }, 464 | { 465 | N: "copy", 466 | Args: nil, 467 | Ret: []Type{BT{"int"}}, 468 | }, 469 | { 470 | N: "len", 471 | Args: nil, 472 | Ret: []Type{BT{"int"}}, 473 | }, 474 | { 475 | N: "min", 476 | Args: nil, 477 | Ret: nil, 478 | }, 479 | { 480 | N: "max", 481 | Args: nil, 482 | Ret: nil, 483 | }, 484 | } 485 | 486 | var StdlibFuncs = []FuncType{ 487 | // math 488 | { 489 | N: "math.Max", 490 | Args: []Type{BT{"float64"}, BT{"float64"}}, 491 | Ret: []Type{BT{"float64"}}, 492 | }, 493 | { 494 | N: "math.NaN", 495 | Args: []Type{}, 496 | Ret: []Type{BT{"float64"}}, 497 | }, 498 | { 499 | N: "math.Ldexp", 500 | Args: []Type{BT{"float64"}, BT{"int"}}, 501 | Ret: []Type{BT{"float64"}}, 502 | }, 503 | { 504 | N: "math.Sqrt", 505 | Args: []Type{BT{"float64"}}, 506 | Ret: []Type{BT{"float64"}}, 507 | }, 508 | 509 | // strings 510 | { 511 | N: "strings.Contains", 512 | Args: []Type{BT{"string"}, BT{"string"}}, 513 | Ret: []Type{BT{"bool"}}, 514 | }, 515 | { 516 | N: "strings.Join", 517 | Args: []Type{ArrayType{BT{"string"}}, BT{"string"}}, 518 | Ret: []Type{BT{"string"}}, 519 | }, 520 | { 521 | N: "strings.TrimFunc", 522 | Args: []Type{ 523 | BT{"string"}, 524 | FuncType{ 525 | Args: []Type{BT{"rune"}}, 526 | Ret: []Type{BT{"bool"}}, 527 | Local: true, 528 | }, 529 | }, 530 | Ret: []Type{BT{"string"}}, 531 | }, 532 | 533 | // reflect 534 | { 535 | N: "reflect.DeepEqual", 536 | Args: nil, 537 | Ret: []Type{BT{"bool"}}, 538 | }, 539 | 540 | // unsafe 541 | { 542 | N: "unsafe.Sizeof", 543 | Args: nil, 544 | Ret: []Type{BT{"uintptr"}}, 545 | }, 546 | { 547 | N: "unsafe.Alignof", 548 | Args: nil, 549 | Ret: []Type{BT{"uintptr"}}, 550 | }, 551 | { 552 | N: "unsafe.Offsetof", 553 | Args: nil, 554 | Ret: []Type{BT{"uintptr"}}, 555 | }, 556 | { 557 | N: "unsafe.SliceData", 558 | Args: nil, 559 | Ret: nil, 560 | }, 561 | { 562 | N: "unsafe.String", 563 | Args: []Type{PointerType{BT{"byte"}}, BT{"int"}}, 564 | Ret: []Type{BT{"string"}}, 565 | }, 566 | { 567 | N: "unsafe.StringData", 568 | Args: []Type{BT{"string"}}, 569 | Ret: []Type{PointerType{BT{"byte"}}}, 570 | }, 571 | } 572 | 573 | func MakeAtomicFuncs() []Variable { 574 | 575 | types := []string{"uint32", "uint64", "uintptr"} 576 | vs := []Variable{} 577 | 578 | for _, t := range types { 579 | for _, fun := range []string{"atomic.Add", "atomic.Swap"} { 580 | f := FuncType{ 581 | N: fun + strings.Title(t), 582 | Args: []Type{PointerOf(BT{t}), BT{t}}, 583 | Ret: []Type{BT{t}}, 584 | } 585 | 586 | vs = append(vs, Variable{f, &ast.Ident{Name: f.N}}) 587 | } 588 | } 589 | 590 | for _, t := range types { 591 | f := FuncType{ 592 | N: "atomic.Load" + strings.Title(t), 593 | Args: []Type{PointerOf(BT{t})}, 594 | Ret: []Type{BT{t}}, 595 | } 596 | 597 | vs = append(vs, Variable{f, &ast.Ident{Name: f.N}}) 598 | } 599 | 600 | return vs 601 | } 602 | 603 | // -------------------------------- 604 | // Chan 605 | // -------------------------------- 606 | 607 | type ChanType struct { 608 | T Type 609 | } 610 | 611 | func (t ChanType) Comparable() bool { 612 | return false 613 | } 614 | 615 | func (t ChanType) Ast() ast.Expr { 616 | return &ast.ChanType{ 617 | Dir: 3, 618 | Value: t.Base().Ast(), 619 | } 620 | } 621 | 622 | func (ct ChanType) Base() Type { 623 | return ct.T 624 | } 625 | 626 | func (t ChanType) Contains(t2 Type) bool { 627 | if t.Equal(t2) { 628 | return true 629 | } else { 630 | return t.Base().Contains(t2) 631 | } 632 | } 633 | 634 | func (t ChanType) Equal(t2 Type) bool { 635 | if t2, ok := t2.(ChanType); !ok { 636 | return false 637 | } else { 638 | return t.Base().Equal(t2.Base()) 639 | } 640 | } 641 | 642 | func (ct ChanType) Name() string { 643 | return "chan " + ct.T.Name() 644 | } 645 | 646 | func (ct ChanType) Sliceable() bool { 647 | return false 648 | } 649 | 650 | func ChanOf(t Type) ChanType { 651 | return ChanType{t} 652 | } 653 | 654 | // -------------------------------- 655 | // Map 656 | // -------------------------------- 657 | 658 | type MapType struct { 659 | KeyT, ValueT Type 660 | } 661 | 662 | func (t MapType) Comparable() bool { 663 | return false 664 | } 665 | 666 | func (t MapType) Ast() ast.Expr { 667 | return &ast.MapType{ 668 | Key: t.KeyT.Ast(), 669 | Value: t.ValueT.Ast(), 670 | } 671 | } 672 | 673 | func (t MapType) Contains(t2 Type) bool { 674 | if t.Equal(t2) { 675 | return true 676 | } 677 | 678 | return t.ValueT.Contains(t2) 679 | } 680 | 681 | func (t MapType) Equal(t2 Type) bool { 682 | if t2, ok := t2.(MapType); !ok { 683 | return false 684 | } else { 685 | return t.KeyT.Equal(t2.KeyT) && t.ValueT.Equal(t2.ValueT) 686 | } 687 | } 688 | 689 | func (mt MapType) Name() string { 690 | return "map[" + mt.KeyT.Name() + "]" + mt.ValueT.Name() 691 | } 692 | 693 | func (mt MapType) Sliceable() bool { 694 | return true 695 | } 696 | 697 | func MapOf(kt, vt Type) MapType { 698 | return MapType{kt, vt} 699 | } 700 | 701 | // -------------------------------- 702 | // InterfaceType 703 | // -------------------------------- 704 | type InterfaceType struct { 705 | Methods []Method 706 | } 707 | 708 | type Method struct { 709 | Name *ast.Ident 710 | Func FuncType 711 | } 712 | 713 | func (m Method) Equal(m2 Method) bool { 714 | return m.Name.Name == m2.Name.Name && 715 | m.Func.Equal(m2.Func) 716 | } 717 | 718 | func (t InterfaceType) Comparable() bool { return true } 719 | 720 | func (t InterfaceType) Ast() ast.Expr { 721 | e := &ast.InterfaceType{Methods: &ast.FieldList{}} 722 | for _, m := range t.Methods { 723 | fld := &ast.Field{ 724 | Names: []*ast.Ident{m.Name}, 725 | Type: m.Func.Ast(), 726 | } 727 | e.Methods.List = append(e.Methods.List, fld) 728 | } 729 | return e 730 | } 731 | 732 | func (t InterfaceType) Equal(t2 Type) bool { 733 | if t2, ok := t2.(InterfaceType); !ok { 734 | return false 735 | } else { 736 | if len(t.Methods) != len(t2.Methods) { 737 | return false 738 | } 739 | for i, m := range t.Methods { 740 | if !m.Equal(t2.Methods[i]) { 741 | return false 742 | } 743 | } 744 | return true 745 | } 746 | } 747 | 748 | func (t InterfaceType) Name() string { 749 | var buf bytes.Buffer 750 | format.Node(&buf, token.NewFileSet(), t.Ast()) 751 | return buf.String() 752 | } 753 | 754 | func (t InterfaceType) Sliceable() bool { return false } 755 | 756 | func (t InterfaceType) Contains(t2 Type) bool { 757 | if t.Equal(t2) { 758 | return true 759 | } 760 | 761 | for _, m := range t.Methods { 762 | if m.Func.Contains(t2) { 763 | return true 764 | } 765 | } 766 | return false 767 | } 768 | 769 | // -------------------------------- 770 | // Constraint 771 | // -------------------------------- 772 | 773 | // type I0 { <---- N 774 | // 775 | // int | string <-- Types 776 | // } 777 | type Constraint struct { 778 | N *ast.Ident 779 | Types []Type 780 | } 781 | 782 | func (c Constraint) Comparable() bool { 783 | for _, t := range c.Types { 784 | if !t.Comparable() { 785 | return false 786 | } 787 | } 788 | return true 789 | } 790 | 791 | func (c Constraint) Ast() ast.Expr { 792 | return c.N 793 | } 794 | 795 | func (c Constraint) Equal(t Type) bool { 796 | if t2, ok := t.(Constraint); !ok { 797 | return false 798 | } else { 799 | if len(c.Types) != len(t2.Types) { 800 | return false 801 | } 802 | for i := range c.Types { 803 | if !c.Types[i].Equal(t2.Types[i]) { // TODO(alb): fix, needs sorting 804 | return false 805 | } 806 | } 807 | return true 808 | } 809 | } 810 | 811 | func (c Constraint) Name() string { 812 | return c.N.Name 813 | } 814 | 815 | func (c Constraint) Sliceable() bool { 816 | return false 817 | } 818 | 819 | func (c Constraint) Contains(t Type) bool { 820 | return c.Equal(t) 821 | } 822 | 823 | func (c Constraint) String() string { 824 | str := "{" + c.N.Name + " " 825 | for _, t := range c.Types { 826 | str += t.Name() + "|" 827 | } 828 | str = str[:len(str)-1] + "}" 829 | return str 830 | } 831 | 832 | // -------------------------------- 833 | // TypeParam 834 | // -------------------------------- 835 | 836 | type TypeParam struct { 837 | N *ast.Ident 838 | Constraint Constraint 839 | } 840 | 841 | func (tp TypeParam) Comparable() bool { 842 | return tp.Constraint.Comparable() 843 | 844 | } 845 | 846 | func (tp TypeParam) Ast() ast.Expr { 847 | return tp.N 848 | } 849 | 850 | func (tp TypeParam) Equal(t Type) bool { 851 | if t2, ok := t.(TypeParam); !ok { 852 | return false 853 | } else { 854 | return tp.N.Name == t2.N.Name 855 | } 856 | } 857 | 858 | func (tp TypeParam) Name() string { 859 | return tp.N.Name 860 | } 861 | 862 | func (tp TypeParam) Sliceable() bool { 863 | return false 864 | } 865 | 866 | func (tp TypeParam) Contains(t Type) bool { 867 | return tp.Equal(t) 868 | } 869 | 870 | func (tp TypeParam) HasLiterals() bool { 871 | for _, t := range tp.Constraint.Types { 872 | if !IsNumeric(t) { 873 | return false 874 | } 875 | } 876 | return true 877 | } 878 | 879 | func MakeTypeParam(v Variable) TypeParam { 880 | return TypeParam{N: v.Name, Constraint: v.Type.(Constraint)} 881 | } 882 | 883 | // ------------------------------------ // 884 | // preallocated // 885 | // ------------------------------------ // 886 | 887 | var Idents = map[string]*ast.Ident{ 888 | "bool": &ast.Ident{Name: "bool"}, 889 | "byte": &ast.Ident{Name: "byte"}, 890 | "int": &ast.Ident{Name: "int"}, 891 | "int8": &ast.Ident{Name: "int8"}, 892 | "int16": &ast.Ident{Name: "int16"}, 893 | "int32": &ast.Ident{Name: "int32"}, 894 | "int64": &ast.Ident{Name: "int64"}, 895 | "uint": &ast.Ident{Name: "uint"}, 896 | "uintptr": &ast.Ident{Name: "uintptr"}, 897 | "float32": &ast.Ident{Name: "float32"}, 898 | "float64": &ast.Ident{Name: "float64"}, 899 | "complex128": &ast.Ident{Name: "complex128"}, 900 | "rune": &ast.Ident{Name: "rune"}, 901 | "string": &ast.Ident{Name: "string"}, 902 | } 903 | 904 | func TypeIdent(t string) *ast.Ident { 905 | if i, ok := Idents[t]; ok { 906 | return i 907 | } else { 908 | return &ast.Ident{Name: t} 909 | } 910 | } 911 | 912 | var AppendIdent = &ast.Ident{Name: "append"} 913 | var CopyIdent = &ast.Ident{Name: "copy"} 914 | var LenIdent = &ast.Ident{Name: "len"} 915 | var MakeIdent = &ast.Ident{Name: "make"} 916 | var CloseIdent = &ast.Ident{Name: "close"} 917 | var ClearIdent = &ast.Ident{Name: "clear"} 918 | var SizeofIdent = &ast.Ident{Name: "Sizeof"} 919 | var TrueIdent = &ast.Ident{Name: "true"} 920 | var FalseIdent = &ast.Ident{Name: "false"} 921 | 922 | // ------------------------------------ 923 | // Ops 924 | // ------------------------------------ 925 | 926 | func UnaryOps(t Type) []token.Token { 927 | switch t2 := t.(type) { 928 | case BasicType: 929 | switch t.Name() { 930 | case "byte", "uint32", "uint64", "uint", "uintptr": 931 | return []token.Token{token.ADD} 932 | case "int", "rune", "int8", "int16", "int32", "int64": 933 | return []token.Token{token.ADD, token.SUB, token.XOR} 934 | case "float32", "float64", "complex128": 935 | return []token.Token{token.ADD, token.SUB} 936 | case "bool": 937 | return []token.Token{token.NOT} 938 | case "string", "any": 939 | return []token.Token{} 940 | default: 941 | panic("Unhandled BasicType " + t.Name()) 942 | } 943 | case TypeParam: 944 | return t2.CommonOps(UnaryOps) 945 | default: 946 | return []token.Token{} 947 | } 948 | } 949 | 950 | func BinOps(t Type) []token.Token { 951 | switch t2 := t.(type) { 952 | 953 | case BasicType: 954 | switch t.Name() { 955 | case "byte", "uint32", "uint64", "uint", "int8", "int16", "int32", "int64": 956 | return []token.Token{ 957 | token.ADD, token.AND, token.AND_NOT, token.MUL, 958 | token.OR, token.QUO, token.REM, token.SHL, token.SHR, 959 | token.SUB, token.XOR, 960 | } 961 | case "uintptr": 962 | return []token.Token{ 963 | token.ADD, token.AND, token.AND_NOT, token.OR, token.XOR, 964 | } 965 | case "int": 966 | // We can't generate shifts for ints, because int expressions 967 | // are used as args for float64() conversions, and in this: 968 | // 969 | // var i int = 2 970 | // float64(8 >> i) 971 | // 972 | // 8 is actually of type float64; because, from the spec: 973 | // 974 | // If the left operand of a non-constant shift expression is 975 | // an untyped constant, it is first implicitly converted to 976 | // the type it would assume if the shift expression were 977 | // replaced by its left operand alone. 978 | // 979 | // ans apparently in float64(8), 8 is a float64. So 980 | // 981 | // float64(8 >> i) 982 | // 983 | // fails to compile with error: 984 | // 985 | // invalid operation: 8 >> i (shift of type float64) 986 | return []token.Token{ 987 | token.ADD, token.AND, token.AND_NOT, token.MUL, 988 | token.OR, token.QUO, token.REM, /*token.SHL, token.SHR,*/ 989 | token.SUB, token.XOR, 990 | } 991 | case "rune": 992 | return []token.Token{ 993 | token.ADD, token.AND, token.AND_NOT, 994 | token.OR, token.SHR, token.SUB, token.XOR, 995 | } 996 | case "float32", "float64": 997 | return []token.Token{token.ADD, token.SUB, token.MUL, token.QUO} 998 | case "complex128": 999 | return []token.Token{token.ADD, token.SUB, token.MUL} 1000 | case "bool": 1001 | return []token.Token{token.LAND, token.LOR} 1002 | case "string": 1003 | return []token.Token{token.ADD} 1004 | case "any": 1005 | return []token.Token{} 1006 | default: 1007 | panic("Unhandled BasicType " + t.Name()) 1008 | } 1009 | 1010 | case TypeParam: 1011 | return t2.CommonOps(BinOps) 1012 | 1013 | default: 1014 | return []token.Token{} 1015 | } 1016 | } 1017 | 1018 | func (t TypeParam) CommonOps(fn func(t Type) []token.Token) []token.Token { 1019 | // TODO(alb): cache this 1020 | m := make(map[token.Token]int) 1021 | for _, st := range t.Constraint.Types { 1022 | for _, t2 := range fn(st) { 1023 | m[t2]++ 1024 | } 1025 | } 1026 | res := make([]token.Token, 0, 8) 1027 | for i, k := range m { 1028 | if k == len(t.Constraint.Types) { 1029 | res = append(res, i) 1030 | } 1031 | } 1032 | return res 1033 | } 1034 | 1035 | // returns a (T)(arg) cast 1036 | func CastToType(t Type, arg ast.Expr) *ast.CallExpr { 1037 | return &ast.CallExpr{ 1038 | Fun: &ast.ParenExpr{X: t.Ast()}, 1039 | Args: []ast.Expr{arg}, 1040 | } 1041 | } 1042 | --------------------------------------------------------------------------------