diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 3b15c018505..89458667997 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -129,6 +129,136 @@ func TestBuiltinIdentifiersShadowing(t *testing.T) { } } +func TestConvertTo(t *testing.T) { + t.Parallel() + + testFunc := func(source, msg string) { + defer func() { + if len(msg) == 0 { + return + } + + r := recover() + + if r == nil { + t.Fail() + } + + err := r.(*PreprocessError) + c := strings.Contains(err.Error(), msg) + if !c { + t.Fatalf(`expected "%s", got "%s"`, msg, r) + } + }() + + m := NewMachine("test", nil) + + n := MustParseFile("main.go", source) + m.RunFiles(n) + m.RunMain() + } + + type cases struct { + source string + msg string + } + + tests := []cases{ + { + `package test + +func main() { + const a int = -1 + println(uint(a)) +}`, + `test/main.go:5:13: cannot convert constant of type IntKind to UintKind`, + }, + { + `package test + +func main() { + const a int = -1 + println(uint8(a)) +}`, + `test/main.go:5:13: cannot convert constant of type IntKind to Uint8Kind`, + }, + { + `package test + +func main() { + const a int = -1 + println(uint16(a)) +}`, + `test/main.go:5:13: cannot convert constant of type IntKind to Uint16Kind`, + }, + { + `package test + +func main() { + const a int = -1 + println(uint32(a)) +}`, + `test/main.go:5:13: cannot convert constant of type IntKind to Uint32Kind`, + }, + { + `package test + +func main() { + const a int = -1 + println(uint64(a)) +}`, + `test/main.go:5:13: cannot convert constant of type IntKind to Uint64Kind`, + }, + { + `package test + +func main() { + const a float32 = 1.5 + println(int32(a)) +}`, + `test/main.go:5:13: cannot convert constant of type Float32Kind to Int32Kind`, + }, + { + `package test + +func main() { + println(int32(1.5)) +}`, + `test/main.go:4:13: cannot convert (const (1.5 bigdec)) to integer type`, + }, + { + `package test + +func main() { + const a float64 = 1.5 + println(int64(a)) +}`, + `test/main.go:5:13: cannot convert constant of type Float64Kind to Int64Kind`, + }, + { + `package test + +func main() { + println(int64(1.5)) +}`, + `test/main.go:4:13: cannot convert (const (1.5 bigdec)) to integer type`, + }, + { + `package test + + func main() { + const f = float64(1.0) + println(int64(f)) + }`, + ``, + }, + } + + for _, tc := range tests { + testFunc(tc.source, tc.msg) + } +} + // run empty main(). func TestRunEmptyMain(t *testing.T) { t.Parallel() diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index b661d693304..b614e72e945 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -800,6 +800,6 @@ func (m *Machine) doOpFuncLit() { func (m *Machine) doOpConvert() { xv := m.PopValue() t := m.PopValue().GetType() - ConvertTo(m.Alloc, m.Store, xv, t) + ConvertTo(m.Alloc, m.Store, xv, t, false) m.PushValue(*xv) } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index b7c22e0b9f6..85a846535ce 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -3656,7 +3656,7 @@ func convertConst(store Store, last BlockNode, cx *ConstExpr, t Type) { setConstAttrs(cx) } else if t != nil { // e.g. a named type or uint8 type to int for indexing. - ConvertTo(nilAllocator, store, &cx.TypedValue, t) + ConvertTo(nilAllocator, store, &cx.TypedValue, t, true) setConstAttrs(cx) } } diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index e4141772d98..8e27bcbcbdb 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -1207,7 +1207,7 @@ func (tv *TypedValue) SetInt(n int) { func (tv *TypedValue) ConvertGetInt() int { var store Store = nil // not used - ConvertTo(nilAllocator, store, tv, IntType) + ConvertTo(nilAllocator, store, tv, IntType, false) return tv.GetInt() } diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index 9ec3427ed8f..df93144b4e7 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -13,7 +13,7 @@ import ( // t cannot be nil or untyped or DataByteType. // the conversion is forced and overflow/underflow is ignored. // TODO: return error, and let caller also print the file and line. -func ConvertTo(alloc *Allocator, store Store, tv *TypedValue, t Type) { +func ConvertTo(alloc *Allocator, store Store, tv *TypedValue, t Type, isConst bool) { if debug { if t == nil { panic("ConvertTo() requires non-nil type") @@ -47,7 +47,7 @@ func ConvertTo(alloc *Allocator, store Store, tv *TypedValue, t Type) { // both NativeType, use reflect to assert. // convert go-native to gno type (shallow). *tv = go2GnoValue2(alloc, store, tv.V.(*NativeValue).Value, false) - ConvertTo(alloc, store, tv, t) + ConvertTo(alloc, store, tv, t, isConst) return } } else { @@ -92,6 +92,17 @@ GNO_CASE: tv.T = t // simple conversion. return } + + validate := func(from Kind, to Kind, cmp func() bool) { + if isConst { + msg := fmt.Sprintf("cannot convert constant of type %s to %s\n", from, to) + if cmp != nil && cmp() { + return + } + panic(msg) + } + } + switch tvk { case IntKind: switch k { @@ -100,14 +111,20 @@ GNO_CASE: tv.T = t tv.SetInt(x) case Int8Kind: + validate(IntKind, Int8Kind, func() bool { return tv.GetInt() >= math.MinInt8 && tv.GetInt() <= math.MaxInt8 }) + x := int8(tv.GetInt()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(IntKind, Int16Kind, func() bool { return tv.GetInt() >= math.MinInt16 && tv.GetInt() <= math.MaxInt16 }) + x := int16(tv.GetInt()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(IntKind, Int32Kind, func() bool { return tv.GetInt() >= math.MinInt32 && tv.GetInt() <= math.MaxInt32 }) + x := int32(tv.GetInt()) tv.T = t tv.SetInt32(x) @@ -116,22 +133,32 @@ GNO_CASE: tv.T = t tv.SetInt64(x) case UintKind: + validate(IntKind, UintKind, func() bool { return tv.GetInt() >= 0 }) + x := uint(tv.GetInt()) tv.T = t tv.SetUint(x) case Uint8Kind: + validate(IntKind, Uint8Kind, func() bool { return tv.GetInt() >= 0 && tv.GetInt() <= math.MaxUint8 }) + x := uint8(tv.GetInt()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(IntKind, Uint16Kind, func() bool { return tv.GetInt() >= 0 && tv.GetInt() <= math.MaxUint16 }) + x := uint16(tv.GetInt()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(IntKind, Uint32Kind, func() bool { return tv.GetInt() >= 0 && tv.GetInt() <= math.MaxUint32 }) + x := uint32(tv.GetInt()) tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(IntKind, Uint64Kind, func() bool { return tv.GetInt() >= 0 }) + x := uint64(tv.GetInt()) tv.T = t tv.SetUint64(x) @@ -144,6 +171,7 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(IntKind, StringKind, nil) tv.V = alloc.NewString(string(rune(tv.GetInt()))) tv.T = t tv.ClearNum() @@ -175,22 +203,32 @@ GNO_CASE: tv.T = t tv.SetInt64(x) case UintKind: + validate(Int8Kind, UintKind, func() bool { return tv.GetInt8() >= 0 }) + x := uint(tv.GetInt8()) tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Int8Kind, Uint8Kind, func() bool { return tv.GetInt8() >= 0 }) + x := uint8(tv.GetInt8()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Int8Kind, Uint16Kind, func() bool { return tv.GetInt8() >= 0 }) + x := uint16(tv.GetInt8()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Int8Kind, Uint32Kind, func() bool { return tv.GetInt8() >= 0 }) + x := uint32(tv.GetInt8()) tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(Int8Kind, Uint64Kind, func() bool { return tv.GetInt8() >= 0 }) + x := uint64(tv.GetInt8()) tv.T = t tv.SetUint64(x) @@ -218,6 +256,8 @@ GNO_CASE: tv.T = t tv.SetInt(x) case Int8Kind: + validate(Int16Kind, Int8Kind, func() bool { return tv.GetInt16() >= math.MinInt8 && tv.GetInt16() <= math.MaxInt8 }) + x := int8(tv.GetInt16()) tv.T = t tv.SetInt8(x) @@ -234,22 +274,32 @@ GNO_CASE: tv.T = t tv.SetInt64(x) case UintKind: + validate(Int16Kind, UintKind, func() bool { return tv.GetInt16() >= 0 }) + x := uint(tv.GetInt16()) tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Int16Kind, Uint8Kind, func() bool { return tv.GetInt16() >= 0 && tv.GetInt16() <= math.MaxUint8 }) + x := uint8(tv.GetInt16()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Int16Kind, Uint16Kind, func() bool { return tv.GetInt16() >= 0 }) + x := uint16(tv.GetInt16()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Int16Kind, Uint32Kind, func() bool { return tv.GetInt16() >= 0 }) + x := uint32(tv.GetInt16()) tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(Int16Kind, Uint64Kind, func() bool { return tv.GetInt16() >= 0 }) + x := uint64(tv.GetInt16()) tv.T = t tv.SetUint64(x) @@ -262,6 +312,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Int16Kind, StringKind, nil) + tv.V = alloc.NewString(string(rune(tv.GetInt16()))) tv.T = t tv.ClearNum() @@ -277,10 +329,14 @@ GNO_CASE: tv.T = t tv.SetInt(x) case Int8Kind: + validate(Int32Kind, Int8Kind, func() bool { return tv.GetInt32() >= math.MinInt8 && tv.GetInt32() <= math.MaxInt8 }) + x := int8(tv.GetInt32()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Int32Kind, Int16Kind, func() bool { return tv.GetInt32() >= math.MinInt16 && tv.GetInt32() <= math.MaxInt16 }) + x := int16(tv.GetInt32()) tv.T = t tv.SetInt16(x) @@ -293,22 +349,32 @@ GNO_CASE: tv.T = t tv.SetInt64(x) case UintKind: + validate(Int32Kind, UintKind, func() bool { return tv.GetInt32() >= 0 }) + x := uint(tv.GetInt32()) tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Int32Kind, Uint8Kind, func() bool { return tv.GetInt32() >= 0 && tv.GetInt32() <= math.MaxUint8 }) + x := uint8(tv.GetInt32()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Int32Kind, Uint16Kind, func() bool { return tv.GetInt32() >= 0 && tv.GetInt32() <= math.MaxUint16 }) + x := uint16(tv.GetInt32()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Int32Kind, Uint32Kind, func() bool { return tv.GetInt32() >= 0 }) + x := uint32(tv.GetInt32()) tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(Int32Kind, Uint64Kind, func() bool { return tv.GetInt32() >= 0 }) + x := uint64(tv.GetInt32()) tv.T = t tv.SetUint64(x) @@ -321,6 +387,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Int32Kind, StringKind, nil) + tv.V = alloc.NewString(string(tv.GetInt32())) tv.T = t tv.ClearNum() @@ -336,14 +404,20 @@ GNO_CASE: tv.T = t tv.SetInt(x) case Int8Kind: + validate(Int64Kind, Int8Kind, func() bool { return tv.GetInt64() >= math.MinInt8 && tv.GetInt64() <= math.MaxInt8 }) + x := int8(tv.GetInt64()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Int64Kind, Int16Kind, func() bool { return tv.GetInt64() >= math.MinInt16 && tv.GetInt64() <= math.MaxInt16 }) + x := int16(tv.GetInt64()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Int64Kind, Int32Kind, func() bool { return tv.GetInt64() >= math.MinInt32 && tv.GetInt64() <= math.MaxInt32 }) + x := int32(tv.GetInt64()) tv.T = t tv.SetInt32(x) @@ -352,22 +426,32 @@ GNO_CASE: tv.T = t tv.SetInt64(x) case UintKind: + validate(Int64Kind, UintKind, func() bool { return tv.GetInt64() >= 0 && uint(tv.GetInt64()) <= math.MaxUint }) + x := uint(tv.GetInt64()) tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Int64Kind, Uint8Kind, func() bool { return tv.GetInt64() >= 0 && tv.GetInt64() <= math.MaxUint8 }) + x := uint8(tv.GetInt64()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Int64Kind, Uint16Kind, func() bool { return tv.GetInt64() >= 0 && tv.GetInt64() <= math.MaxUint16 }) + x := uint16(tv.GetInt64()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Int64Kind, Uint32Kind, func() bool { return tv.GetInt64() >= 0 && tv.GetInt64() <= math.MaxUint32 }) + x := uint32(tv.GetInt64()) tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(Int64Kind, Uint64Kind, func() bool { return tv.GetInt64() >= 0 }) + x := uint64(tv.GetInt64()) tv.T = t tv.SetUint64(x) @@ -380,6 +464,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Int64Kind, Uint64Kind, nil) + tv.V = alloc.NewString(string(rune(tv.GetInt64()))) tv.T = t tv.ClearNum() @@ -391,22 +477,32 @@ GNO_CASE: case UintKind: switch k { case IntKind: + validate(UintKind, IntKind, func() bool { return tv.GetUint() <= math.MaxInt }) + x := int(tv.GetUint()) tv.T = t tv.SetInt(x) case Int8Kind: + validate(UintKind, Int8Kind, func() bool { return tv.GetUint() <= math.MaxInt8 }) + x := int8(tv.GetUint()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(UintKind, Int16Kind, func() bool { return tv.GetUint() <= math.MaxInt16 }) + x := int16(tv.GetUint()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(UintKind, Int32Kind, func() bool { return tv.GetUint() <= math.MaxInt32 }) + x := int32(tv.GetUint()) tv.T = t tv.SetInt32(x) case Int64Kind: + validate(UintKind, Int64Kind, func() bool { return tv.GetUint() <= math.MaxInt64 }) + x := int64(tv.GetUint()) tv.T = t tv.SetInt64(x) @@ -415,14 +511,20 @@ GNO_CASE: tv.T = t tv.SetUint(x) case Uint8Kind: + validate(UintKind, Uint8Kind, func() bool { return tv.GetUint() <= math.MaxUint8 }) + x := uint8(tv.GetUint()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(UintKind, Uint16Kind, func() bool { return tv.GetUint() <= math.MaxUint16 }) + x := uint16(tv.GetUint()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(UintKind, Uint32Kind, func() bool { return tv.GetUint() <= math.MaxUint32 }) + x := uint32(tv.GetUint()) tv.T = t tv.SetUint32(x) @@ -439,6 +541,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(UintKind, StringKind, nil) + tv.V = alloc.NewString(string(rune(tv.GetUint()))) tv.T = t tv.ClearNum() @@ -454,18 +558,26 @@ GNO_CASE: tv.T = t tv.SetInt(x) case Int8Kind: + validate(Uint8Kind, Int8Kind, func() bool { return tv.GetUint8() <= math.MaxInt8 }) + x := int8(tv.GetUint8()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Uint8Kind, Int16Kind, func() bool { return int(tv.GetUint8()) <= math.MaxInt16 }) + x := int16(tv.GetUint8()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Uint8Kind, Int32Kind, func() bool { return int(tv.GetUint8()) <= math.MaxInt32 }) + x := int32(tv.GetUint8()) tv.T = t tv.SetInt32(x) case Int64Kind: + validate(Uint8Kind, Int64Kind, func() bool { return int(tv.GetUint8()) <= math.MaxInt64 }) + x := int64(tv.GetUint8()) tv.T = t tv.SetInt64(x) @@ -498,6 +610,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Uint8Kind, StringKind, nil) + tv.V = alloc.NewString(string(rune(tv.GetUint8()))) tv.T = t tv.ClearNum() @@ -513,18 +627,26 @@ GNO_CASE: tv.T = t tv.SetInt(x) case Int8Kind: + validate(Uint16Kind, Int8Kind, func() bool { return tv.GetUint16() <= math.MaxInt8 }) + x := int8(tv.GetUint16()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Uint16Kind, Int16Kind, func() bool { return tv.GetUint16() <= math.MaxInt16 }) + x := int16(tv.GetUint16()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Uint16Kind, Int32Kind, func() bool { return int(tv.GetUint16()) <= math.MaxInt32 }) + x := int32(tv.GetUint16()) tv.T = t tv.SetInt32(x) case Int64Kind: + validate(Uint16Kind, Int64Kind, func() bool { return int(tv.GetUint16()) <= math.MaxInt64 }) + x := int64(tv.GetUint16()) tv.T = t tv.SetInt64(x) @@ -533,6 +655,8 @@ GNO_CASE: tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Uint16Kind, Uint8Kind, func() bool { return int(tv.GetUint16()) <= math.MaxUint8 }) + x := uint8(tv.GetUint16()) tv.T = t tv.SetUint8(x) @@ -557,6 +681,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Uint16Kind, StringKind, nil) + tv.V = alloc.NewString(string(rune(tv.GetUint16()))) tv.T = t tv.ClearNum() @@ -568,18 +694,26 @@ GNO_CASE: case Uint32Kind: switch k { case IntKind: + validate(Uint32Kind, IntKind, func() bool { return int(tv.GetUint32()) <= math.MaxInt }) + x := int(tv.GetUint32()) tv.T = t tv.SetInt(x) case Int8Kind: + validate(Uint32Kind, Int8Kind, func() bool { return int(tv.GetUint32()) <= math.MaxInt8 }) + x := int8(tv.GetUint32()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Uint32Kind, Int16Kind, func() bool { return int(tv.GetUint32()) <= math.MaxInt16 }) + x := int16(tv.GetUint32()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Uint32Kind, Int32Kind, func() bool { return int(tv.GetUint32()) <= math.MaxInt32 }) + x := int32(tv.GetUint32()) tv.T = t tv.SetInt32(x) @@ -592,10 +726,14 @@ GNO_CASE: tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Uint32Kind, Uint8Kind, func() bool { return int(tv.GetUint32()) <= math.MaxUint8 }) + x := uint8(tv.GetUint32()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Uint32Kind, Uint16Kind, func() bool { return int(tv.GetUint32()) <= math.MaxUint16 }) + x := uint16(tv.GetUint32()) tv.T = t tv.SetUint16(x) @@ -616,6 +754,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Uint32Kind, StringKind, nil) + tv.V = alloc.NewString(string(rune(tv.GetUint32()))) tv.T = t tv.ClearNum() @@ -627,38 +767,56 @@ GNO_CASE: case Uint64Kind: switch k { case IntKind: + validate(Uint64Kind, IntKind, func() bool { return int(tv.GetUint64()) <= math.MaxInt }) + x := int(tv.GetUint64()) tv.T = t tv.SetInt(x) case Int8Kind: + validate(Uint64Kind, Int8Kind, func() bool { return int(tv.GetUint64()) <= math.MaxInt8 }) + x := int8(tv.GetUint64()) tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Uint64Kind, Int16Kind, func() bool { return int(tv.GetUint64()) <= math.MaxInt16 }) + x := int16(tv.GetUint64()) tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Uint64Kind, Int32Kind, func() bool { return int(tv.GetUint64()) <= math.MaxInt32 }) + x := int32(tv.GetUint64()) tv.T = t tv.SetInt32(x) case Int64Kind: + validate(Uint64Kind, Int64Kind, func() bool { return int(tv.GetUint64()) <= math.MaxInt64 }) + x := int64(tv.GetUint64()) tv.T = t tv.SetInt64(x) case UintKind: + validate(Uint64Kind, UintKind, func() bool { return tv.GetUint64() <= math.MaxUint }) + x := uint(tv.GetUint64()) tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Uint64Kind, Uint8Kind, func() bool { return int(tv.GetUint64()) <= math.MaxUint8 }) + x := uint8(tv.GetUint64()) tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Uint64Kind, Uint16Kind, func() bool { return int(tv.GetUint64()) <= math.MaxUint16 }) + x := uint16(tv.GetUint64()) tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Uint64Kind, Uint32Kind, func() bool { return int(tv.GetUint64()) <= math.MaxUint32 }) + x := uint32(tv.GetUint64()) tv.T = t tv.SetUint32(x) @@ -675,6 +833,8 @@ GNO_CASE: tv.T = t tv.SetFloat64(x) case StringKind: + validate(Uint64Kind, StringKind, nil) + tv.V = alloc.NewString(string(rune(tv.GetUint64()))) tv.T = t tv.ClearNum() @@ -686,42 +846,148 @@ GNO_CASE: case Float32Kind: switch k { case IntKind: + validate(Float32Kind, IntKind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt + }) + x := int(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetInt(x) case Int8Kind: + validate(Float32Kind, Int8Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 + }) + x := int8(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Float32Kind, Int16Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 + }) + x := int16(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Float32Kind, Int32Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 + }) + x := int32(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetInt32(x) case Int64Kind: + validate(Float32Kind, Int64Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + return val == trunc + }) + x := int64(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetInt64(x) case UintKind: + validate(Float32Kind, UintKind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return trunc >= 0 && trunc <= math.MaxUint + }) + x := uint(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Float32Kind, Uint8Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 + }) + x := uint8(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Float32Kind, Uint16Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 + }) + x := uint16(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Float32Kind, Uint32Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 + }) + x := uint32(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(Float32Kind, Uint64Kind, func() bool { + val := float64(tv.GetFloat32()) + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return trunc >= 0 && trunc <= math.MaxUint + }) + x := uint64(tv.GetFloat32()) // XXX determinism? tv.T = t tv.SetUint64(x) @@ -741,46 +1007,156 @@ GNO_CASE: case Float64Kind: switch k { case IntKind: + validate(Float64Kind, IntKind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt + }) + x := int(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetInt(x) case Int8Kind: + validate(Float64Kind, Int8Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 + }) + x := int8(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetInt8(x) case Int16Kind: + validate(Float64Kind, Int16Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 + }) + x := int16(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetInt16(x) case Int32Kind: + validate(Float64Kind, Int32Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 + }) + x := int32(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetInt32(x) case Int64Kind: + validate(Float64Kind, Int64Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + return val == trunc + }) + x := int64(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetInt64(x) case UintKind: + validate(Float64Kind, UintKind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return trunc >= 0 && trunc <= math.MaxUint + }) + x := uint(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetUint(x) case Uint8Kind: + validate(Float64Kind, Uint8Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 + }) + x := uint8(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetUint8(x) case Uint16Kind: + validate(Float64Kind, Uint16Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 + }) + x := uint16(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetUint16(x) case Uint32Kind: + validate(Float64Kind, Uint32Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 + }) + x := uint32(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetUint32(x) case Uint64Kind: + validate(Float64Kind, Uint64Kind, func() bool { + val := tv.GetFloat64() + trunc := math.Trunc(val) + + if val != trunc { + return false + } + + return trunc >= 0 && trunc <= math.MaxUint64 + }) + x := uint64(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetUint64(x) case Float32Kind: + validate(Float64Kind, Float32Kind, func() bool { + return tv.GetFloat64() <= math.MaxFloat32 + }) + x := float32(tv.GetFloat64()) // XXX determinism? tv.T = t tv.SetFloat32(x) @@ -923,7 +1299,7 @@ func ConvertUntypedTo(tv *TypedValue, t Type) { ConvertUntypedTo(tv, gnot) // then convert to native value. // NOTE: this should only be called during preprocessing, so no alloc needed. - ConvertTo(nilAllocator, nil, tv, t) + ConvertTo(nilAllocator, nil, tv, t, false) } // special case: simple conversion if t != nil && tv.T.Kind() == t.Kind() { @@ -962,7 +1338,7 @@ func ConvertUntypedTo(tv *TypedValue, t Type) { tv.T = t return } else { - ConvertTo(nilAllocator, nil, tv, t) + ConvertTo(nilAllocator, nil, tv, t, false) } default: panic(fmt.Sprintf( diff --git a/gnovm/tests/files/float1.gno b/gnovm/tests/files/float1.gno index 9eaed64e063..95e130d816e 100644 --- a/gnovm/tests/files/float1.gno +++ b/gnovm/tests/files/float1.gno @@ -1,7 +1,8 @@ package main func main() { - x := int(float64(1.2)) + f := float64(1.2) + x := int(f) println(x) }