Skip to content

Commit d63918f

Browse files
ltzmaxwellthehowlmvertes
authored
fix(gnovm): correct type for shift expression (#1775)
[shift operator where first operand is an untyped bigint always results in a bigint](#1462) is not resolved by #1426, it's fixed by this one. ================================================================= 1. This is a fix to /issues/1462; 3. **NOTE**: This PR should be reviewed following the potential merger of #1426, from which it is both decoupled and dependent. #1426 serves as base branch of this one. 4. **NOTE**: Currently, this PR displays all code including that from #1426, because it is being compared to the master branch instead of differing against #1426 directly. --------- Co-authored-by: Morgan <git@howl.moe> Co-authored-by: Marc Vertes <mvertes@free.fr>
1 parent 912a5db commit d63918f

File tree

117 files changed

+1640
-144
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+1640
-144
lines changed

gnovm/pkg/gnolang/eval_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ func TestEvalFiles(t *testing.T) {
4040
if wantStacktrace != "" && !strings.Contains(stacktrace, wantStacktrace) {
4141
t.Fatalf("unexpected stacktrace\nWant: %s\n Got: %s", wantStacktrace, stacktrace)
4242
}
43-
if wantOut != "" && out != wantOut {
44-
t.Fatalf("unexpected output\nWant: %s\n Got: %s", wantOut, out)
43+
if wantOut != "" && strings.TrimSpace(out) != strings.TrimSpace(wantOut) {
44+
t.Fatalf("unexpected output\nWant: \"%s\"\n Got: \"%s\"", wantOut, out)
4545
}
4646
})
4747

gnovm/pkg/gnolang/nodes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,7 @@ const (
21192119
ATTR_IOTA GnoAttribute = "ATTR_IOTA"
21202120
ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONED"
21212121
ATTR_INJECTED GnoAttribute = "ATTR_INJECTED"
2122+
ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS"
21222123
)
21232124

21242125
var rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]+$`)

gnovm/pkg/gnolang/op_binary.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,7 @@ func xorAssign(lv, rv *TypedValue) {
10971097

10981098
// for doOpShl and doOpShlAssign.
10991099
func shlAssign(lv, rv *TypedValue) {
1100+
rv.AssertNonNegative("runtime error: negative shift amount")
11001101
// set the result in lv.
11011102
// NOTE: baseOf(rv.T) is always UintType.
11021103
switch baseOf(lv.T) {
@@ -1136,6 +1137,7 @@ func shlAssign(lv, rv *TypedValue) {
11361137

11371138
// for doOpShr and doOpShrAssign.
11381139
func shrAssign(lv, rv *TypedValue) {
1140+
rv.AssertNonNegative("runtime error: negative shift amount")
11391141
// set the result in lv.
11401142
// NOTE: baseOf(rv.T) is always UintType.
11411143
switch baseOf(lv.T) {

gnovm/pkg/gnolang/op_call.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ func (m *Machine) doOpPrecall() {
2727
case TypeValue:
2828
// Do not pop type yet.
2929
// No need for frames.
30+
xv := m.PeekValue(1)
31+
if cx.GetAttribute(ATTR_SHIFT_RHS) == true {
32+
xv.AssertNonNegative("runtime error: negative shift amount")
33+
}
34+
3035
m.PushOp(OpConvert)
3136
if debug {
3237
if len(cx.Args) != 1 {

gnovm/pkg/gnolang/preprocess.go

Lines changed: 154 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
10251025
isShift := n.Op == SHL || n.Op == SHR
10261026
if isShift {
10271027
// check LHS type compatibility
1028-
n.checkShiftLhs(lt)
1028+
n.assertShiftExprCompatible1(store, last, lt, rt)
10291029
// checkOrConvert RHS
10301030
if baseOf(rt) != UintType {
10311031
// convert n.Right to (gno) uint type,
@@ -1036,6 +1036,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
10361036
Op: n.Op,
10371037
Right: rn,
10381038
}
1039+
n2.Right.SetAttribute(ATTR_SHIFT_RHS, true)
10391040
resn := Preprocess(store, last, n2)
10401041
return resn, TRANS_CONTINUE
10411042
}
@@ -1097,12 +1098,34 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
10971098
// NOTE: binary operations are always computed in
10981099
// gno, never with reflect.
10991100
} else {
1100-
// convert n.Left to right type.
1101-
checkOrConvertType(store, last, &n.Left, rt, false)
1101+
// right is untyped const, left is not const, typed/untyped
1102+
checkUntypedShiftExpr := func(x Expr) {
1103+
if bx, ok := x.(*BinaryExpr); ok {
1104+
slt := evalStaticTypeOf(store, last, bx.Left)
1105+
if bx.Op == SHL || bx.Op == SHR {
1106+
srt := evalStaticTypeOf(store, last, bx.Right)
1107+
bx.assertShiftExprCompatible1(store, last, slt, srt)
1108+
}
1109+
}
1110+
}
1111+
1112+
if !isUntyped(rt) { // right is typed
1113+
checkOrConvertType(store, last, &n.Left, rt, false)
1114+
} else {
1115+
if shouldSwapOnSpecificity(lt, rt) {
1116+
checkUntypedShiftExpr(n.Right)
1117+
} else {
1118+
checkUntypedShiftExpr(n.Left)
1119+
}
1120+
}
11021121
}
11031122
} else if lcx.T == nil { // LHS is nil.
11041123
// convert n.Left to typed-nil type.
11051124
checkOrConvertType(store, last, &n.Left, rt, false)
1125+
} else {
1126+
if isUntyped(rt) {
1127+
checkOrConvertType(store, last, &n.Right, lt, false)
1128+
}
11061129
}
11071130
} else if ric { // right is const, left is not
11081131
if isUntyped(rcx.T) {
@@ -1134,12 +1157,33 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
11341157
// NOTE: binary operations are always computed in
11351158
// gno, never with reflect.
11361159
} else {
1137-
// convert n.Right to left type.
1138-
checkOrConvertType(store, last, &n.Right, lt, false)
1160+
// right is untyped const, left is not const, typed or untyped
1161+
checkUntypedShiftExpr := func(x Expr) {
1162+
if bx, ok := x.(*BinaryExpr); ok {
1163+
if bx.Op == SHL || bx.Op == SHR {
1164+
srt := evalStaticTypeOf(store, last, bx.Right)
1165+
bx.assertShiftExprCompatible1(store, last, rt, srt)
1166+
}
1167+
}
1168+
}
1169+
// both untyped, e.g. 1<<s != 1.0
1170+
if !isUntyped(lt) { // left is typed
1171+
checkOrConvertType(store, last, &n.Right, lt, false)
1172+
} else { // if one side is untyped shift expression, check type with lower specificity
1173+
if shouldSwapOnSpecificity(lt, rt) {
1174+
checkUntypedShiftExpr(n.Right)
1175+
} else {
1176+
checkUntypedShiftExpr(n.Left)
1177+
}
1178+
}
11391179
}
11401180
} else if rcx.T == nil { // RHS is nil
11411181
// refer to tests/files/types/eql_0f20.gno
11421182
checkOrConvertType(store, last, &n.Right, lt, false)
1183+
} else { // left is not const, right is typed const
1184+
if isUntyped(lt) {
1185+
checkOrConvertType(store, last, &n.Left, rt, false)
1186+
}
11431187
}
11441188
} else {
11451189
// Left not const, Right not const ------------------
@@ -1267,27 +1311,28 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
12671311
panic("type conversion requires single argument")
12681312
}
12691313
n.NumArgs = 1
1270-
if arg0, ok := n.Args[0].(*ConstExpr); ok {
1271-
var constConverted bool
1272-
ct := evalStaticType(store, last, n.Func)
1314+
ct := evalStaticType(store, last, n.Func)
1315+
at := evalStaticTypeOf(store, last, n.Args[0])
1316+
var constConverted bool
1317+
switch arg0 := n.Args[0].(type) {
1318+
case *ConstExpr:
12731319
// As a special case, if a decimal cannot
12741320
// be represented as an integer, it cannot be converted to one,
12751321
// and the error is handled here.
12761322
// Out of bounds errors are usually handled during evalConst().
1277-
switch ct.Kind() {
1278-
case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind,
1279-
UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind,
1280-
BigintKind:
1323+
if isIntNum(ct) {
12811324
if bd, ok := arg0.TypedValue.V.(BigdecValue); ok {
12821325
if !isInteger(bd.V) {
12831326
panic(fmt.Sprintf(
12841327
"cannot convert %s to integer type",
12851328
arg0))
12861329
}
12871330
}
1288-
convertConst(store, last, arg0, ct)
1289-
constConverted = true
1290-
case SliceKind:
1331+
if isNumeric(at) {
1332+
convertConst(store, last, arg0, ct)
1333+
constConverted = true
1334+
}
1335+
} else if ct.Kind() == SliceKind {
12911336
if ct.Elem().Kind() == Uint8Kind { // bypass []byte("xxx")
12921337
n.SetAttribute(ATTR_TYPEOF_VALUE, ct)
12931338
return n, TRANS_CONTINUE
@@ -1298,18 +1343,34 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
12981343
if !constConverted {
12991344
convertConst(store, last, arg0, nil)
13001345
}
1301-
13021346
// evaluate the new expression.
13031347
cx := evalConst(store, last, n)
13041348
// Though cx may be undefined if ct is interface,
13051349
// the ATTR_TYPEOF_VALUE is still interface.
13061350
cx.SetAttribute(ATTR_TYPEOF_VALUE, ct)
13071351
return cx, TRANS_CONTINUE
1308-
} else {
1309-
ct := evalStaticType(store, last, n.Func)
1310-
n.SetAttribute(ATTR_TYPEOF_VALUE, ct)
1311-
return n, TRANS_CONTINUE
1352+
case *BinaryExpr: // special case to evaluate type of binaryExpr/UnaryExpr which has untyped shift nested
1353+
if isUntyped(at) {
1354+
switch arg0.Op {
1355+
case EQL, NEQ, LSS, GTR, LEQ, GEQ:
1356+
assertAssignableTo(at, ct, false)
1357+
break
1358+
default:
1359+
checkOrConvertType(store, last, &n.Args[0], ct, false)
1360+
}
1361+
}
1362+
case *UnaryExpr:
1363+
if isUntyped(at) {
1364+
checkOrConvertType(store, last, &n.Args[0], ct, false)
1365+
}
1366+
default:
1367+
// do nothing
13121368
}
1369+
// general case, for non-const untyped && no nested untyped shift
1370+
// after handling const, and special cases recursively, set the target node type
1371+
// ct := evalStaticType(store, last, n.Func)
1372+
n.SetAttribute(ATTR_TYPEOF_VALUE, ct)
1373+
return n, TRANS_CONTINUE
13131374
default:
13141375
panic(fmt.Sprintf(
13151376
"unexpected func type %v (%v)",
@@ -1479,7 +1540,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
14791540
}
14801541
}
14811542
} else {
1482-
for i := range n.Args {
1543+
for i := range n.Args { // iterate args
14831544
if hasVarg {
14841545
if (len(spts) - 1) <= i {
14851546
if isVarg {
@@ -1929,6 +1990,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
19291990
} else {
19301991
last.Define(ln, anyValue(rt))
19311992
}
1993+
// if rhs is untyped
1994+
if isUntyped(rt) {
1995+
checkOrConvertType(store, last, &n.Rhs[i], nil, false)
1996+
}
19321997
}
19331998
}
19341999
} else { // ASSIGN, or assignment operation (+=, -=, <<=, etc.)
@@ -2018,9 +2083,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
20182083
}
20192084
} else { // len(Lhs) == len(Rhs)
20202085
if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN {
2021-
if len(n.Lhs) != 1 || len(n.Rhs) != 1 {
2022-
panic("should not happen")
2023-
}
20242086
// Special case if shift assign <<= or >>=.
20252087
convertType(store, last, &n.Rhs[0], UintType)
20262088
} else if n.Op == ADD_ASSIGN || n.Op == SUB_ASSIGN || n.Op == MUL_ASSIGN || n.Op == QUO_ASSIGN || n.Op == REM_ASSIGN {
@@ -2281,10 +2343,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
22812343
vt := evalStaticTypeOf(store, last, vx)
22822344
sts[i] = vt
22832345
}
2284-
} else {
2346+
} else { // T is nil, n not const
22852347
// convert n.Value to default type.
22862348
for i, vx := range n.Values {
2287-
convertIfConst(store, last, vx)
2349+
if cx, ok := vx.(*ConstExpr); ok {
2350+
convertConst(store, last, cx, nil)
2351+
// convertIfConst(store, last, vx)
2352+
} else {
2353+
checkOrConvertType(store, last, &vx, nil, false)
2354+
}
22882355
vt := evalStaticTypeOf(store, last, vx)
22892356
sts[i] = vt
22902357
}
@@ -2840,9 +2907,25 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
28402907
assertAssignableTo(cx.T, t, autoNative)
28412908
}
28422909
} else if bx, ok := (*x).(*BinaryExpr); ok && (bx.Op == SHL || bx.Op == SHR) {
2843-
// "push" expected type into shift binary's left operand. recursively.
2844-
checkOrConvertType(store, last, &bx.Left, t, autoNative)
2845-
} else if *x != nil { // XXX if x != nil && t != nil {
2910+
xt := evalStaticTypeOf(store, last, *x)
2911+
if debug {
2912+
debug.Printf("shift, xt: %v, Op: %v, t: %v \n", xt, bx.Op, t)
2913+
}
2914+
if isUntyped(xt) {
2915+
// check assignable first, see: types/shift_b6.gno
2916+
assertAssignableTo(xt, t, autoNative)
2917+
2918+
if t == nil || t.Kind() == InterfaceKind {
2919+
t = defaultTypeOf(xt)
2920+
}
2921+
2922+
bx.assertShiftExprCompatible2(t)
2923+
checkOrConvertType(store, last, &bx.Left, t, autoNative)
2924+
} else {
2925+
assertAssignableTo(xt, t, autoNative)
2926+
}
2927+
return
2928+
} else if *x != nil {
28462929
xt := evalStaticTypeOf(store, last, *x)
28472930
if t != nil {
28482931
assertAssignableTo(xt, t, autoNative)
@@ -2853,19 +2936,53 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
28532936
switch bx.Op {
28542937
case ADD, SUB, MUL, QUO, REM, BAND, BOR, XOR,
28552938
BAND_NOT, LAND, LOR:
2856-
// push t into bx.Left and bx.Right
2857-
checkOrConvertType(store, last, &bx.Left, t, autoNative)
2858-
checkOrConvertType(store, last, &bx.Right, t, autoNative)
2859-
return
2860-
case SHL, SHR:
2861-
// push t into bx.Left
2862-
checkOrConvertType(store, last, &bx.Left, t, autoNative)
2939+
lt := evalStaticTypeOf(store, last, bx.Left)
2940+
rt := evalStaticTypeOf(store, last, bx.Right)
2941+
if t != nil {
2942+
// push t into bx.Left and bx.Right
2943+
checkOrConvertType(store, last, &bx.Left, t, autoNative)
2944+
checkOrConvertType(store, last, &bx.Right, t, autoNative)
2945+
return
2946+
} else {
2947+
if shouldSwapOnSpecificity(lt, rt) {
2948+
// e.g. 1.0<<s + 1
2949+
// The expression '1.0<<s' does not trigger assertions of
2950+
// incompatible types when evaluated alone.
2951+
// However, when evaluating the full expression '1.0<<s + 1'
2952+
// without a specific context type, '1.0<<s' is checked against
2953+
// its default type, the BigDecKind, will trigger assertion failure.
2954+
// so here in checkOrConvertType, shift expression is "finally" checked.
2955+
checkOrConvertType(store, last, &bx.Left, lt, autoNative)
2956+
checkOrConvertType(store, last, &bx.Right, lt, autoNative)
2957+
} else {
2958+
checkOrConvertType(store, last, &bx.Left, rt, autoNative)
2959+
checkOrConvertType(store, last, &bx.Right, rt, autoNative)
2960+
}
2961+
}
28632962
return
28642963
case EQL, LSS, GTR, NEQ, LEQ, GEQ:
2865-
// do nothing
2964+
lt := evalStaticTypeOf(store, last, bx.Left)
2965+
rt := evalStaticTypeOf(store, last, bx.Right)
2966+
if shouldSwapOnSpecificity(lt, rt) {
2967+
checkOrConvertType(store, last, &bx.Left, lt, autoNative)
2968+
checkOrConvertType(store, last, &bx.Right, lt, autoNative)
2969+
} else {
2970+
checkOrConvertType(store, last, &bx.Left, rt, autoNative)
2971+
checkOrConvertType(store, last, &bx.Right, rt, autoNative)
2972+
}
28662973
default:
28672974
// do nothing
28682975
}
2976+
} else if ux, ok := (*x).(*UnaryExpr); ok {
2977+
xt := evalStaticTypeOf(store, last, *x)
2978+
// check assignable first
2979+
assertAssignableTo(xt, t, autoNative)
2980+
2981+
if t == nil || t.Kind() == InterfaceKind {
2982+
t = defaultTypeOf(xt)
2983+
}
2984+
checkOrConvertType(store, last, &ux.X, t, autoNative)
2985+
return
28692986
}
28702987
}
28712988
}
@@ -2891,9 +3008,6 @@ func convertType(store Store, last BlockNode, x *Expr, t Type) {
28913008
if t == nil {
28923009
t = defaultTypeOf(xt)
28933010
}
2894-
if debug {
2895-
debug.Printf("default type of t: %v \n", t)
2896-
}
28973011
// convert x to destination type t
28983012
doConvertType(store, last, x, t)
28993013
} else {

0 commit comments

Comments
 (0)