github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/gosmith/stmt.go (about) 1 package main 2 3 import ( 4 _ "fmt" 5 ) 6 7 func (smith *Smith) initStatements() { 8 smith.statements = []func(){ 9 smith.stmtOas, 10 smith.stmtAs, 11 smith.stmtInc, 12 smith.stmtIf, 13 smith.stmtFor, 14 smith.stmtSend, 15 smith.stmtRecv, 16 smith.stmtSelect, 17 smith.stmtSwitchExpr, 18 smith.stmtSwitchType, 19 smith.stmtTypeDecl, 20 smith.stmtVarDecl, 21 smith.stmtCall, 22 smith.stmtReturn, 23 smith.stmtBreak, 24 smith.stmtContinue, 25 smith.stmtGoto, 26 smith.stmtSink, 27 } 28 } 29 30 func (smith *Smith) genStatement() { 31 if smith.stmtCount >= NStatements { 32 return 33 } 34 smith.exprCount = 0 35 smith.stmtCount++ 36 smith.statements[smith.rnd(len(smith.statements))]() 37 } 38 39 func (smith *Smith) stmtOas() { 40 list := smith.atypeList(TraitAny) 41 str, vars := smith.fmtOasVarList(list) 42 smith.line("%v := %v", str, smith.fmtRvalueList(list)) 43 for _, v := range vars { 44 smith.defineVar(v.id, v.typ) 45 } 46 } 47 48 func (smith *Smith) stmtReturn() { 49 smith.line("return %v", smith.fmtRvalueList(smith.curFunc.rets)) 50 } 51 52 func (smith *Smith) stmtAs() { 53 types := smith.atypeList(TraitAny) 54 smith.line("%v = %v", smith.fmtLvalueList(types), smith.fmtRvalueList(types)) 55 } 56 57 func (smith *Smith) stmtInc() { 58 smith.line("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++")) 59 } 60 61 func (smith *Smith) stmtIf() { 62 smith.enterBlock(true) 63 smith.enterBlock(true) 64 if smith.rndBool() { 65 smith.line("if %v {", smith.rvalue(smith.atype(ClassBoolean))) 66 } else { 67 smith.line("if %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean))) 68 } 69 smith.genBlock() 70 if smith.rndBool() { 71 smith.line("} else {") 72 smith.genBlock() 73 } 74 smith.leaveBlock() 75 smith.line("}") 76 smith.leaveBlock() 77 } 78 79 func (smith *Smith) stmtFor() { 80 smith.enterBlock(true) 81 smith.enterBlock(true) 82 smith.curBlock.isBreakable = true 83 smith.curBlock.isContinuable = true 84 var vars []*Var 85 switch smith.choice("simple", "complex", "range") { 86 case "simple": 87 smith.line("for %v {", smith.rvalue(smith.atype(ClassBoolean))) 88 case "complex": 89 smith.line("for %v; %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean)), smith.stmtSimple(false, nil)) 90 case "range": 91 switch smith.choice("slice", "string", "channel", "map") { 92 case "slice": 93 t := smith.atype(TraitAny) 94 s := smith.rvalue(smith.sliceOf(t)) 95 switch smith.choice("one", "two", "oneDecl", "twoDecl") { 96 case "one": 97 smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s) 98 case "two": 99 smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(t), s) 100 case "oneDecl": 101 id := smith.newId("Var") 102 smith.line("for %v := range %v {", id, s) 103 vars = append(vars, &Var{id: id, typ: smith.intType}) 104 case "twoDecl": 105 types := []*Type{smith.intType, t} 106 str := "" 107 str, vars = smith.fmtOasVarList(types) 108 smith.line("for %v := range %v {", str, s) 109 default: 110 panic("bad") 111 } 112 case "string": 113 s := smith.rvalue(smith.stringType) 114 switch smith.choice("one", "two", "oneDecl", "twoDecl") { 115 case "one": 116 smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s) 117 case "two": 118 smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(smith.runeType), s) 119 case "oneDecl": 120 id := smith.newId("Var") 121 smith.line("for %v := range %v {", id, s) 122 vars = append(vars, &Var{id: id, typ: smith.intType}) 123 case "twoDecl": 124 types := []*Type{smith.intType, smith.runeType} 125 str := "" 126 str, vars = smith.fmtOasVarList(types) 127 smith.line("for %v := range %v {", str, s) 128 default: 129 panic("bad") 130 } 131 case "channel": 132 cht := smith.atype(ClassChan) 133 ch := smith.rvalue(cht) 134 switch smith.choice("one", "oneDecl") { 135 case "one": 136 smith.line("for %v = range %v {", smith.lvalueOrBlank(cht.ktyp), ch) 137 case "oneDecl": 138 id := smith.newId("Var") 139 smith.line("for %v := range %v {", id, ch) 140 vars = append(vars, &Var{id: id, typ: cht.ktyp}) 141 default: 142 panic("bad") 143 } 144 case "map": 145 t := smith.atype(ClassMap) 146 m := smith.rvalue(t) 147 switch smith.choice("one", "two", "oneDecl", "twoDecl") { 148 case "one": 149 smith.line("for %v = range %v {", smith.lvalueOrBlank(t.ktyp), m) 150 case "two": 151 smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(t.vtyp), m) 152 case "oneDecl": 153 id := smith.newId("Var") 154 smith.line("for %v := range %v {", id, m) 155 vars = append(vars, &Var{id: id, typ: t.ktyp}) 156 case "twoDecl": 157 types := []*Type{t.ktyp, t.vtyp} 158 str := "" 159 str, vars = smith.fmtOasVarList(types) 160 smith.line("for %v := range %v {", str, m) 161 default: 162 panic("bad") 163 } 164 default: 165 panic("bad") 166 } 167 default: 168 panic("bad") 169 } 170 smith.enterBlock(true) 171 if len(vars) > 0 { 172 smith.line("") 173 for _, v := range vars { 174 smith.defineVar(v.id, v.typ) 175 } 176 } 177 smith.genBlock() 178 smith.leaveBlock() 179 smith.leaveBlock() 180 smith.line("}") 181 smith.leaveBlock() 182 } 183 184 func (smith *Smith) stmtSimple(oas bool, newVars *[]*Var) string { 185 // We emit a fake statement in "oas", so make sure that nothing can be inserted in between. 186 if smith.curBlock.extendable { 187 panic("bad") 188 } 189 // "send" crashes gccgo with random errors too frequently. 190 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61273 191 switch smith.choice("empty", "inc", "assign", "oas", "send", "expr") { 192 case "empty": 193 return "" 194 case "inc": 195 return F("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++")) 196 case "assign": 197 list := smith.atypeList(TraitAny) 198 return F("%v = %v", smith.fmtLvalueList(list), smith.fmtRvalueList(list)) 199 case "oas": 200 if !oas { 201 return "" 202 } 203 list := smith.atypeList(TraitAny) 204 str, vars := smith.fmtOasVarList(list) 205 if newVars != nil { 206 *newVars = vars 207 } 208 res := F("%v := %v", str, smith.fmtRvalueList(list)) 209 smith.line("") 210 for _, v := range vars { 211 smith.defineVar(v.id, v.typ) 212 } 213 return res 214 case "send": 215 t := smith.atype(TraitSendable) 216 return F("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp)) 217 case "expr": 218 return "" 219 default: 220 panic("bad") 221 } 222 } 223 224 func (smith *Smith) stmtSend() { 225 t := smith.atype(TraitSendable) 226 smith.line("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp)) 227 } 228 229 func (smith *Smith) stmtRecv() { 230 t := smith.atype(TraitReceivable) 231 ch := smith.rvalue(t) 232 switch smith.choice("normal", "decl") { 233 case "normal": 234 smith.line("%v, %v = <-%v", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(smith.boolType), ch) 235 case "decl": 236 vv := smith.newId("Var") 237 ok := smith.newId("Var") 238 smith.line("%v, %v := <-%v", vv, ok, ch) 239 smith.defineVar(vv, t.ktyp) 240 smith.defineVar(ok, smith.boolType) 241 default: 242 panic("bad") 243 } 244 } 245 246 func (smith *Smith) stmtTypeDecl() { 247 id := smith.newId("Type") 248 t := smith.atype(TraitAny) 249 smith.line("type %v %v", id, t.id) 250 251 newTyp := new(Type) 252 *newTyp = *t 253 newTyp.id = id 254 newTyp.namedUserType = true 255 if t.class == ClassStruct { 256 newTyp.literal = func() string { 257 // replace struct name with new type id 258 l := t.literal() 259 l = l[len(t.id)+1:] 260 return "(" + id + l 261 } 262 newTyp.complexLiteral = func() string { 263 // replace struct name with new type id 264 l := t.complexLiteral() 265 l = l[len(t.id)+1:] 266 return "(" + id + l 267 } 268 } else { 269 newTyp.literal = func() string { 270 return F("%v(%v)", id, t.literal()) 271 } 272 if t.complexLiteral != nil { 273 newTyp.complexLiteral = func() string { 274 return F("%v(%v)", id, t.complexLiteral()) 275 } 276 } 277 } 278 smith.defineType(newTyp) 279 } 280 281 func (smith *Smith) stmtVarDecl() { 282 id := smith.newId("Var") 283 t := smith.atype(TraitAny) 284 smith.line("var %v %v = %v", id, t.id, smith.rvalue(t)) 285 smith.defineVar(id, t) 286 } 287 288 func (smith *Smith) stmtSelect() { 289 smith.enterBlock(true) 290 smith.line("select {") 291 for smith.rnd(5) != 0 { 292 smith.enterBlock(true) 293 elem := smith.atype(TraitAny) 294 cht := smith.chanOf(elem) 295 ch := smith.rvalue(cht) 296 if smith.rndBool() { 297 smith.line("case %v <- %v:", ch, smith.rvalue(elem)) 298 } else { 299 switch smith.choice("one", "two", "oneDecl", "twoDecl") { 300 case "one": 301 smith.line("case %v = <-%v:", smith.lvalueOrBlank(elem), ch) 302 case "two": 303 smith.line("case %v, %v = <-%v:", smith.lvalueOrBlank(elem), smith.lvalueOrBlank(smith.boolType), ch) 304 case "oneDecl": 305 vv := smith.newId("Var") 306 smith.line("case %v := <-%v:", vv, ch) 307 smith.defineVar(vv, elem) 308 case "twoDecl": 309 vv := smith.newId("Var") 310 ok := smith.newId("Var") 311 smith.line("case %v, %v := <-%v:", vv, ok, ch) 312 smith.defineVar(vv, elem) 313 smith.defineVar(ok, smith.boolType) 314 default: 315 panic("bad") 316 } 317 } 318 smith.genBlock() 319 smith.leaveBlock() 320 } 321 if smith.rndBool() { 322 smith.enterBlock(true) 323 smith.line("default:") 324 smith.genBlock() 325 smith.leaveBlock() 326 } 327 smith.line("}") 328 smith.leaveBlock() 329 } 330 331 func (smith *Smith) stmtSwitchExpr() { 332 var t *Type 333 cond := "" 334 if smith.rndBool() { 335 t = smith.atype(TraitComparable) 336 cond = smith.rvalue(t) 337 } else { 338 t = smith.boolType 339 } 340 smith.enterBlock(true) 341 smith.enterBlock(true) 342 smith.curBlock.isBreakable = true 343 var vars []*Var 344 if smith.rndBool() { 345 smith.line("switch %v {", cond) 346 } else { 347 smith.line("switch %v; %v {", smith.stmtSimple(true, &vars), cond) 348 } 349 // TODO: we generate at most one case, because if we generate more, 350 // we can generate two cases with equal constants. 351 fallthru := false 352 if smith.rndBool() { 353 smith.enterBlock(true) 354 smith.line("case %v:", smith.rvalue(t)) 355 smith.genBlock() 356 smith.leaveBlock() 357 if smith.rndBool() { 358 fallthru = true 359 smith.line("fallthrough") 360 } 361 } 362 if fallthru || len(vars) > 0 || smith.rndBool() { 363 smith.enterBlock(true) 364 smith.line("default:") 365 smith.genBlock() 366 for _, v := range vars { 367 smith.line("_ = %v", v.id) 368 v.used = true 369 } 370 smith.leaveBlock() 371 } 372 smith.leaveBlock() 373 smith.line("}") 374 smith.leaveBlock() 375 } 376 377 func (smith *Smith) stmtSwitchType() { 378 cond := smith.lvalue(smith.atype(TraitAny)) 379 smith.enterBlock(true) 380 smith.curBlock.isBreakable = true 381 smith.line("switch COND := (interface{})(%v); COND.(type) {", cond) 382 if smith.rndBool() { 383 smith.enterBlock(true) 384 smith.line("case %v:", smith.atype(TraitAny).id) 385 smith.genBlock() 386 smith.leaveBlock() 387 } 388 if smith.rndBool() { 389 smith.enterBlock(true) 390 smith.line("default:") 391 smith.genBlock() 392 smith.leaveBlock() 393 } 394 smith.line("}") 395 smith.leaveBlock() 396 } 397 398 func (smith *Smith) stmtCall() { 399 if smith.rndBool() { 400 smith.stmtCallBuiltin() 401 } 402 t := smith.atype(ClassFunction) 403 prefix := smith.choice("", "go", "defer") 404 smith.line("%v %v(%v)", prefix, smith.rvalue(t), smith.fmtRvalueList(t.styp)) 405 } 406 407 func (smith *Smith) stmtCallBuiltin() { 408 prefix := smith.choice("", "go", "defer") 409 switch fn := smith.choice("close", "copy", "delete", "panic", "print", "println", "recover"); fn { 410 case "close": 411 smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(ClassChan))) 412 case "copy": 413 smith.line("%v %v", prefix, smith.exprCopySlice()) 414 case "delete": 415 t := smith.atype(ClassMap) 416 smith.line("%v %v(%v, %v)", prefix, fn, smith.rvalue(t), smith.rvalue(t.ktyp)) 417 case "panic": 418 smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(TraitAny))) 419 case "print": 420 fallthrough 421 case "println": 422 list := smith.atypeList(TraitPrintable) 423 smith.line("%v %v(%v)", prefix, fn, smith.fmtRvalueList(list)) 424 case "recover": 425 smith.line("%v %v()", prefix, fn) 426 default: 427 panic("bad") 428 } 429 } 430 431 func (smith *Smith) stmtBreak() { 432 if !smith.curBlock.isBreakable { 433 return 434 } 435 smith.line("break") 436 } 437 438 func (smith *Smith) stmtContinue() { 439 if !smith.curBlock.isContinuable { 440 return 441 } 442 smith.line("continue") 443 } 444 445 func (smith *Smith) stmtGoto() { 446 // TODO: support goto down 447 id := smith.materializeGotoLabel() 448 smith.line("goto %v", id) 449 } 450 451 func (smith *Smith) stmtSink() { 452 // Makes var escape. 453 smith.line("SINK = %v", smith.exprVar(smith.atype(TraitAny))) 454 }