diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index e9bfb55d52f..3edcfd7a25f 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -2299,6 +2299,11 @@ func (sb *StaticBlock) GetFuncNodeForExpr(store Store, fne Expr) (FuncNode, erro // could go further and store preprocessed constant results here too. See // "anyValue()" and "asValue()" for usage. func (sb *StaticBlock) Define(n Name, tv TypedValue) { + if tv.T == nil { + panic(fmt.Sprintf( + "StaticBlock.Define(%s) requires non-nil tv.T; use Reserve() for placeholder slots", + n)) + } sb.Define2(false, n, tv.T, tv, NameSource{}) } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 7387746b3fb..967483df088 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1042,7 +1042,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { n.Cases[i] = toConstTypeExpr(last, cx, ct) // maybe type-switch def. if ss.VarName != "" { - if len(n.Cases) == 1 { + if len(n.Cases) == 1 && ct != nil { // If there is only 1 case, the // define applies with type. // (re-definition). @@ -1050,7 +1050,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { ss.VarName, anyValue(ct)) } else { // If there are 2 or more - // cases, the type is the tag type. + // cases, or the sole case is nil, + // the type is the tag type. tt := evalStaticTypeOf(store, last, ss.X) last.Define( ss.VarName, anyValue(tt)) diff --git a/gnovm/tests/files/typeswitch1.gno b/gnovm/tests/files/typeswitch1.gno new file mode 100644 index 00000000000..00c16fb242a --- /dev/null +++ b/gnovm/tests/files/typeswitch1.gno @@ -0,0 +1,87 @@ +// Test simple type switches on basic types, including `case nil:` as +// the sole case (the regression that motivated this filetest — Gno +// preprocess previously panicked "name xx not declared" because the +// single-case branch registered the type-switch var with a nil static +// type when ct was nil). + +package main + +import "fmt" + +const ( + a = iota + b + c + d + e +) + +var x = []int{1, 2, 3} + +func f(x int, len *byte) { + *len = byte(x) +} + +func whatis(x interface{}) string { + switch xx := x.(type) { + default: + return fmt.Sprint("default ", xx) + case int, int8, int16, int32: + return fmt.Sprint("signed ", xx) + case int64: + return fmt.Sprint("signed64 ", int64(xx)) + case uint, uint8, uint16, uint32: + return fmt.Sprint("unsigned ", xx) + case uint64: + return fmt.Sprint("unsigned64 ", uint64(xx)) + case nil: + return fmt.Sprint("nil ", xx) + } + panic("not reached") +} + +func whatis1(x interface{}) string { + xx := x + switch xx.(type) { + default: + return fmt.Sprint("default ", xx) + case int, int8, int16, int32: + return fmt.Sprint("signed ", xx) + case int64: + return fmt.Sprint("signed64 ", xx.(int64)) + case uint, uint8, uint16, uint32: + return fmt.Sprint("unsigned ", xx) + case uint64: + return fmt.Sprint("unsigned64 ", xx.(uint64)) + case nil: + return fmt.Sprint("nil ", xx) + } + panic("not reached") +} + +func check(x interface{}, s string) { + w := whatis(x) + if w != s { + fmt.Println("whatis", x, "=>", w, "!=", s) + panic("fail") + } + + w = whatis1(x) + if w != s { + fmt.Println("whatis1", x, "=>", w, "!=", s) + panic("fail") + } +} + +func main() { + check(1, "signed 1") + check(uint(1), "unsigned 1") + check(int64(1), "signed64 1") + check(uint64(1), "unsigned64 1") + check(1.5, "default 1.5") + check(nil, "nil ") + fmt.Println("ok") +} + +// Output: +// ok