diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index 8dbbb553de..433d38544e 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -254,6 +254,34 @@ func (s *exprSwitch) walk(sw *Node) { } } + // Given "switch string(byteslice)", + // with all cases being constants (or the default case), + // use a zero-cost alias of the byte slice. + // In theory, we could be more aggressive, + // allowing any side-effect-free expressions in cases, + // but it's a bit tricky because some of that information + // is unavailable due to the introduction of temporaries during order. + // Restricting to constants is simple and probably powerful enough. + // Do this before calling walkexpr on cond, + // because walkexpr will lower the string + // conversion into a runtime call. + // See issue 24937 for more discussion. + if cond.Op == OARRAYBYTESTR { + ok := true + for _, cas := range sw.List.Slice() { + if cas.Op != OCASE { + Fatalf("switch string(byteslice) bad op: %v", cas.Op) + } + if cas.Left != nil && !Isconst(cas.Left, CTSTR) { + ok = false + break + } + } + if ok { + cond.Op = OARRAYBYTESTRTMP + } + } + cond = walkexpr(cond, &sw.Ninit) t := sw.Type if t == nil { diff --git a/test/fixedbugs/issue24937.go b/test/fixedbugs/issue24937.go new file mode 100644 index 0000000000..7d8460f61e --- /dev/null +++ b/test/fixedbugs/issue24937.go @@ -0,0 +1,15 @@ +// run + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + x := []byte{'a'} + switch string(x) { + case func() string { x[0] = 'b'; return "b" }(): + panic("FAIL") + } +}