Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
if isUntyped(at) {
switch arg0.Op {
case EQL, NEQ, LSS, GTR, LEQ, GEQ:
assertAssignableTo(n, at, ct)
mustAssignableTo(n, at, ct)
default:
checkOrConvertType(store, last, n, &n.Args[0], ct)
}
Expand Down Expand Up @@ -1441,7 +1441,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
_, atIface := atBase.(*InterfaceType)
if ctIface {
// e.g. <iface type>(...)
assertAssignableTo(n, at, ct)
mustAssignableTo(n, at, ct)
// The conversion is legal, set the target type.
n.SetAttribute(ATTR_TYPEOF_VALUE, ct)
return n, TRANS_CONTINUE
Expand Down Expand Up @@ -1819,12 +1819,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
for i, tv := range argTVs {
if hasVarg {
if (len(spts) - 1) <= i {
assertAssignableTo(n, tv.T, spts[len(spts)-1].Type.Elem())
mustAssignableTo(n, tv.T, spts[len(spts)-1].Type.Elem())
} else {
assertAssignableTo(n, tv.T, spts[i].Type)
mustAssignableTo(n, tv.T, spts[i].Type)
}
} else {
assertAssignableTo(n, tv.T, spts[i].Type)
mustAssignableTo(n, tv.T, spts[i].Type)
}
}
} else {
Expand Down Expand Up @@ -2835,7 +2835,10 @@ func parseMultipleAssignFromOneExpr(
for i := range nameExprs {
if st != nil {
tt := tuple.Elts[i]
if checkAssignableTo(n, tt, st) != nil {
if err := checkAssignableTo(n, tt, st); err != nil {
if debug {
debug.Printf("checkAssignableTo fail: %v\n", err)
}
panic(
fmt.Sprintf(
"cannot use %v (value of type %s) as %s value in assignment",
Expand Down Expand Up @@ -4069,15 +4072,15 @@ func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type) {
}
if cx, ok := (*x).(*ConstExpr); ok {
// e.g. int(1) == int8(1)
assertAssignableTo(n, cx.T, t)
mustAssignableTo(n, cx.T, t)
} else if bx, ok := (*x).(*BinaryExpr); ok && (bx.Op == SHL || bx.Op == SHR) {
xt := evalStaticTypeOf(store, last, *x)
if debug {
debug.Printf("shift, xt: %v, Op: %v, t: %v \n", xt, bx.Op, t)
}
if isUntyped(xt) {
// check assignable first, see: types/shift_b6.gno
assertAssignableTo(n, xt, t)
mustAssignableTo(n, xt, t)

if t == nil || t.Kind() == InterfaceKind {
t = defaultTypeOf(xt)
Expand All @@ -4086,13 +4089,13 @@ func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type) {
bx.assertShiftExprCompatible2(t)
checkOrConvertType(store, last, n, &bx.Left, t)
} else {
assertAssignableTo(n, xt, t)
mustAssignableTo(n, xt, t)
}
return
} else if *x != nil {
xt := evalStaticTypeOf(store, last, *x)
if t != nil {
assertAssignableTo(n, xt, t)
mustAssignableTo(n, xt, t)
}
if isUntyped(xt) {
// Push type into expr if qualifying binary expr.
Expand Down Expand Up @@ -4144,7 +4147,7 @@ func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type) {
} else if ux, ok := (*x).(*UnaryExpr); ok {
xt := evalStaticTypeOf(store, last, *x)
// check assignable first
assertAssignableTo(n, xt, t)
mustAssignableTo(n, xt, t)

if t == nil || t.Kind() == InterfaceKind {
t = defaultTypeOf(xt)
Expand Down Expand Up @@ -4241,7 +4244,7 @@ func convertIfConst(store Store, last BlockNode, n Node, x Expr) {
func convertConst(store Store, last BlockNode, n Node, cx *ConstExpr, t Type) {
if t != nil && t.Kind() == InterfaceKind {
if cx.T != nil {
assertAssignableTo(n, cx.T, t)
mustAssignableTo(n, cx.T, t)
}
t = nil // signifies to convert to default type.
}
Expand Down
32 changes: 22 additions & 10 deletions gnovm/pkg/gnolang/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,12 @@ func checkSame(at, bt Type, msg string) error {
return nil
}

func assertAssignableTo(n Node, xt, dt Type) {
func mustAssignableTo(n Node, xt, dt Type) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: but why must overt assert
IMO, i use must when i expect something in return like: "mustGetEntity" and assert when i don't expect a return but it's how i see it, just is there a reason behind this change ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for me, must is more of a convention from Go stdlibs, e.g.

regexp.MustCompile(...) (Panics if regex is invalid)
template.Must(...) (Panics if template parsing fails)

while assert is always used in unit test, e.g. testify/assert.

err := checkAssignableTo(n, xt, dt)
if err != nil {
if debug {
debug.Printf("checkAssignableTo fail: %v\n", err)
}
panic(err.Error())
}
}
Expand Down Expand Up @@ -734,7 +737,13 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
case EQL, NEQ:
assertComparable(xt, dt)
if !isUntyped(xt) && !isUntyped(dt) {
assertAssignableTo(x, xt, dt)
err := checkAssignableTo(x, xt, dt)
if err != nil {
if debug {
debug.Printf("checkAssignableTo fail: %v\n", err)
}
panic(fmt.Sprintf("invalid operation: %v (mismatched types %v and %v)", x, xt, dt))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-wise, we should maybe avoid panicking with the whole expression, as it might be very deep. what about making the error "types %v and %v are not comparable"?

}
Comment on lines +740 to +746
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log low-level error and return user friendly info, which is more in line with Go.

}
case LSS, LEQ, GTR, GEQ:
if checker, ok := binaryChecker[x.Op]; ok {
Expand Down Expand Up @@ -793,6 +802,9 @@ func (x *BinaryExpr) checkCompatibility(n Node, xt, dt Type, checker func(t Type
if !isUntyped(xt) && !isUntyped(dt) {
err := checkAssignableTo(n, xt, dt)
if err != nil {
if debug {
debug.Printf("checkAssignableTo fail: %v\n", err)
}
if swapped {
panic(fmt.Sprintf("invalid operation: %v (mismatched types %v and %v)", n, dt, untypedNil(xt)))
} else {
Expand Down Expand Up @@ -850,19 +862,19 @@ func (x *RangeStmt) AssertCompatible(store Store, last BlockNode) {
xt := evalStaticTypeOf(store, last, x.X)
switch cxt := xt.(type) {
case *MapType:
assertAssignableTo(x, cxt.Key, kt)
mustAssignableTo(x, cxt.Key, kt)
if vt != nil {
assertAssignableTo(x, cxt.Value, vt)
mustAssignableTo(x, cxt.Value, vt)
}
case *SliceType:
assertIndexTypeIsInt(kt)
if vt != nil {
assertAssignableTo(x, cxt.Elt, vt)
mustAssignableTo(x, cxt.Elt, vt)
}
case *ArrayType:
assertIndexTypeIsInt(kt)
if vt != nil {
assertAssignableTo(x, cxt.Elt, vt)
mustAssignableTo(x, cxt.Elt, vt)
}
case PrimitiveType:
if cxt.Kind() == StringKind {
Expand Down Expand Up @@ -902,7 +914,7 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
assertValidAssignLhs(store, last, lx)
if !isBlankIdentifier(lx) {
lxt := evalStaticTypeOf(store, last, lx)
assertAssignableTo(x, cft.Results[i].Type, lxt)
mustAssignableTo(x, cft.Results[i].Type, lxt)
}
}
}
Expand All @@ -917,7 +929,7 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
if !isBlankIdentifier(x.Lhs[0]) { // see composite3.gno
dt := evalStaticTypeOf(store, last, x.Lhs[0])
ift := evalStaticTypeOf(store, last, cx)
assertAssignableTo(x, ift, dt)
mustAssignableTo(x, ift, dt)
}
// check second value
assertValidAssignLhs(store, last, x.Lhs[1])
Expand All @@ -940,12 +952,12 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
if _, ok := cx.X.(*NameExpr); ok {
rt := evalStaticTypeOf(store, last, cx.X)
if mt, ok := rt.(*MapType); ok {
assertAssignableTo(x, mt.Value, lt)
mustAssignableTo(x, mt.Value, lt)
}
} else if _, ok := cx.X.(*CompositeLitExpr); ok {
cpt := evalStaticTypeOf(store, last, cx.X)
if mt, ok := cpt.(*MapType); ok {
assertAssignableTo(x, mt.Value, lt)
mustAssignableTo(x, mt.Value, lt)
} else {
panic("should not happen")
}
Expand Down
4 changes: 2 additions & 2 deletions gnovm/pkg/gnolang/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2300,7 +2300,7 @@ func specifyType(store Store, n Node, lookup map[Name]Type, tmpl Type, spec Type
generic := ct.Generic[:len(ct.Generic)-len(".Elem()")]
match, ok := lookup[generic]
if ok {
assertAssignableTo(n, spec, match.Elem())
mustAssignableTo(n, spec, match.Elem())
return // ok
} else {
// Panic here, because we don't know whether T
Expand All @@ -2314,7 +2314,7 @@ func specifyType(store Store, n Node, lookup map[Name]Type, tmpl Type, spec Type
} else {
match, ok := lookup[ct.Generic]
if ok {
assertAssignableTo(n, spec, match)
mustAssignableTo(n, spec, match)
return // ok
} else {
if isUntyped(spec) {
Expand Down
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/cmp_iface_5.gno
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func main() {
}

// Error:
// main/cmp_iface_5.gno:19:5-23: int64 does not implement .uverse.error (missing method Error)
// main/cmp_iface_5.gno:19:5-23: invalid operation: errCmp<VPBlock(4,1)> == (const (1 int64)) (mismatched types int64 and .uverse.error)

// TypeCheckError:
// main/cmp_iface_5.gno:19:15: invalid operation: errCmp == int64(1) (mismatched types error and int64)
32 changes: 32 additions & 0 deletions gnovm/tests/files/types/cmp_iface_9.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

type A interface {
F()
}

type B interface {
X()
G()
}

type T struct{}

func (T) F() {}

func (T) G() {}

func (T) X() {}

func main() {
var a A = T{}
var b B = T{}

println(b == a)
println(a == b)
}

// Error:
// main/cmp_iface_9.gno:24:10-16: invalid operation: b<VPBlock(1,1)> == a<VPBlock(1,0)> (mismatched types main.B and main.A)

// TypeCheckError:
// main/cmp_iface_9.gno:24:15: invalid operation: b == a (mismatched types B and A); main/cmp_iface_9.gno:25:15: invalid operation: a == b (mismatched types A and B)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/cmp_pointer.gno
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func main() {
}

// Error:
// main/cmp_pointer.gno:19:27-38: cannot use main.Person as main.Worker without explicit conversion
// main/cmp_pointer.gno:19:27-38: invalid operation: p1<VPBlock(1,0)> == p2Ptr<VPBlock(1,2)> (mismatched types *main.Person and *main.Worker)

// TypeCheckError:
// main/cmp_pointer.gno:19:33: invalid operation: p1 == p2Ptr (mismatched types *Person and *Worker)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/cmp_slice_4.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ func main() {
}

// Error:
// main/cmp_slice_4.gno:6:10-23: cannot use int as string
// main/cmp_slice_4.gno:6:10-23: invalid operation: a<VPBlock(1,1)> == expected<VPBlock(1,0)> (mismatched types int and string)

// TypeCheckError:
// main/cmp_slice_4.gno:6:15: invalid operation: a == expected (mismatched types int and string)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/cmp_struct_b.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func main() {
}

// Error:
// main/cmp_struct_b.gno:15:10-18: cannot use main.foo as main.bar without explicit conversion
// main/cmp_struct_b.gno:15:10-18: invalid operation: fa<VPBlock(1,0)> == bb<VPBlock(1,1)> (mismatched types main.foo and main.bar)

// TypeCheckError:
// main/cmp_struct_b.gno:15:16: invalid operation: fa == bb (mismatched types foo and bar)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/cmp_struct_c1.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func main() {
}

// Error:
// main/cmp_struct_c1.gno:15:10-18: cannot use main.bar as main.foo without explicit conversion
// main/cmp_struct_c1.gno:15:10-18: invalid operation: bb<VPBlock(1,1)> == fa<VPBlock(1,0)> (mismatched types main.bar and main.foo)

// TypeCheckError:
// main/cmp_struct_c1.gno:15:16: invalid operation: bb == fa (mismatched types bar and foo)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/cmp_struct_g.gno
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func main() {
}

// Error:
// main/cmp_struct_g.gno:17:25-31: cannot use main.Person as main.Dog without explicit conversion
// main/cmp_struct_g.gno:17:25-31: invalid operation: a<VPBlock(1,0)> == b<VPBlock(1,1)> (mismatched types main.Person and main.Dog)

// TypeCheckError:
// main/cmp_struct_g.gno:17:30: invalid operation: a == b (mismatched types Person and Dog)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a0.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ func main() {
}

// Error:
// main/eql_0a0.gno:5:10-27: cannot use int as int8
// main/eql_0a0.gno:5:10-27: invalid operation: (const (1 int)) == (const (1 int8)) (mismatched types int and int8)

// TypeCheckError:
// main/eql_0a0.gno:5:20: invalid operation: int(1) == int8(1) (mismatched types int and int8)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a02.gno
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func main() {
}

// Error:
// main/eql_0a02.gno:7:10-21: cannot use *int as string
// main/eql_0a02.gno:7:10-21: invalid operation: intPtr<VPBlock(1,0)> == s<VPBlock(1,1)> (mismatched types *int and string)

// TypeCheckError:
// main/eql_0a02.gno:7:20: invalid operation: intPtr == s (mismatched types *int and string)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a03.gno
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func main() {
}

// Error:
// main/eql_0a03.gno:8:10-22: cannot use int8 as int
// main/eql_0a03.gno:8:10-22: invalid operation: intPtr<VPBlock(1,0)> == &(i<VPBlock(1,1)>) (mismatched types *int8 and *int)

// TypeCheckError:
// main/eql_0a03.gno:8:20: invalid operation: intPtr == &i (mismatched types *int8 and *int)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a1.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ func main() {
}

// Error:
// main/eql_0a1.gno:5:10-27: cannot use int as int8
// main/eql_0a1.gno:5:10-27: invalid operation: (const (1 int)) != (const (1 int8)) (mismatched types int and int8)

// TypeCheckError:
// main/eql_0a1.gno:5:20: invalid operation: int(1) != int8(1) (mismatched types int and int8)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a1a0.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ func main() {
}

// Error:
// main/eql_0a1a0.gno:5:10-24: cannot use uint64 as uint
// main/eql_0a1a0.gno:5:10-24: invalid operation: (const (1 uint64)) == a<VPBlock(1,0)> (mismatched types uint64 and uint)

// TypeCheckError:
// main/eql_0a1a0.gno:5:23: invalid operation: uint64(1) == a (mismatched types uint64 and uint)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a1a1.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ func main() {
}

// Error:
// main/eql_0a1a1.gno:5:10-24: cannot use uint as uint64
// main/eql_0a1a1.gno:5:10-24: invalid operation: a<VPBlock(1,0)> == (const (1 uint64)) (mismatched types uint and uint64)

// TypeCheckError:
// main/eql_0a1a1.gno:5:15: invalid operation: a == uint64(1) (mismatched types uint and uint64)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a1f.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ func main() {
}

// Error:
// main/eql_0a1f.gno:6:10-23: cannot use int as string
// main/eql_0a1f.gno:6:10-23: invalid operation: a<VPBlock(1,1)> == expected<VPBlock(1,0)> (mismatched types int and string)

// TypeCheckError:
// main/eql_0a1f.gno:6:15: invalid operation: a == expected (mismatched types int and string)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a1g.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ func main() {
}

// Error:
// main/eql_0a1g.gno:6:10-16: cannot use int as float32
// main/eql_0a1g.gno:6:10-16: invalid operation: a<VPBlock(1,0)> == b<VPBlock(1,1)> (mismatched types int and float32)

// TypeCheckError:
// main/eql_0a1g.gno:6:15: invalid operation: a == b (mismatched types int and float32)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a2.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {
}

// Error:
// main/eql_0a2.gno:21:10-32: cannot use main.Error1 as main.Error2 without explicit conversion
// main/eql_0a2.gno:21:10-32: invalid operation: (const (0 main.Error1)) == (const (0 main.Error2)) (mismatched types main.Error1 and main.Error2)

// TypeCheckError:
// main/eql_0a2.gno:21:23: invalid operation: Error1(0) == Error2(0) (mismatched types Error1 and Error2)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a3.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {
}

// Error:
// main/eql_0a3.gno:21:10-32: cannot use main.Error1 as main.Error2 without explicit conversion
// main/eql_0a3.gno:21:10-32: invalid operation: (const (0 main.Error1)) != (const (0 main.Error2)) (mismatched types main.Error1 and main.Error2)

// TypeCheckError:
// main/eql_0a3.gno:21:23: invalid operation: Error1(0) != Error2(0) (mismatched types Error1 and Error2)
2 changes: 1 addition & 1 deletion gnovm/tests/files/types/eql_0a4.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {
}

// Error:
// main/eql_0a4.gno:21:10-32: cannot use main.Error1 as main.Error2 without explicit conversion
// main/eql_0a4.gno:21:10-32: invalid operation: (const (0 main.Error1)) != (const (0 main.Error2)) (mismatched types main.Error1 and main.Error2)

// TypeCheckError:
// main/eql_0a4.gno:21:23: invalid operation: Error1(0) != Error2(0) (mismatched types Error1 and Error2)
Loading
Loading