Skip to content

Commit 09362bb

Browse files
omarsythehowlltzmaxwell
authored andcommittedJan 10, 2025
fix(gnovm): improve error message for nil assignment in variable declaration (#3068)
…aration <!-- please provide a detailed description of the changes made in this pull request. --> <details><summary>Contributors' checklist...</summary> - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests </details> --------- Co-authored-by: Morgan <[email protected]> Co-authored-by: ltzmaxwell <[email protected]>
1 parent 4f99472 commit 09362bb

File tree

9 files changed

+200
-138
lines changed

9 files changed

+200
-138
lines changed
 

‎gnovm/pkg/gnolang/preprocess.go

+101-98
Large diffs are not rendered by default.

‎gnovm/pkg/gnolang/type_check.go

+33-20
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ func checkSame(at, bt Type, msg string) error {
208208
return nil
209209
}
210210

211-
func assertAssignableTo(xt, dt Type, autoNative bool) {
212-
err := checkAssignableTo(xt, dt, autoNative)
211+
func assertAssignableTo(n Node, xt, dt Type, autoNative bool) {
212+
err := checkAssignableTo(n, xt, dt, autoNative)
213213
if err != nil {
214214
panic(err.Error())
215215
}
@@ -282,7 +282,7 @@ func checkValDefineMismatch(n Node) {
282282
// Assert that xt can be assigned as dt (dest type).
283283
// If autoNative is true, a broad range of xt can match against
284284
// a target native dt type, if and only if dt is a native type.
285-
func checkAssignableTo(xt, dt Type, autoNative bool) error {
285+
func checkAssignableTo(n Node, xt, dt Type, autoNative bool) error {
286286
if debug {
287287
debug.Printf("checkAssignableTo, xt: %v dt: %v \n", xt, dt)
288288
}
@@ -292,7 +292,20 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
292292
return nil
293293
}
294294
if !maybeNil(dt) {
295-
panic(fmt.Sprintf("invalid operation, nil can not be compared to %v", dt))
295+
switch n := n.(type) {
296+
case *ValueDecl:
297+
panic(fmt.Sprintf("cannot use nil as %v value in variable declaration", dt))
298+
case *AssignStmt:
299+
panic(fmt.Sprintf("cannot use nil as %v value in assignment", dt))
300+
case *CompositeLitExpr:
301+
panic(fmt.Sprintf("cannot use nil as %v value in array, slice literal or map literal", dt))
302+
case *CallExpr:
303+
panic(fmt.Sprintf("cannot use nil as %v value in argument to %v", dt, n.Func))
304+
case *BinaryExpr:
305+
panic(fmt.Sprintf("invalid operation: %v (mismatched types %v and untyped nil)", n, dt))
306+
default:
307+
panic(fmt.Sprintf("cannot use nil as %v value", dt))
308+
}
296309
}
297310
return nil
298311
} else if dt == nil { // _ = xxx, assign8.gno, 0f31. else cases?
@@ -504,7 +517,7 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
504517
}
505518
case *PointerType: // case 4 from here on
506519
if pt, ok := xt.(*PointerType); ok {
507-
return checkAssignableTo(pt.Elt, cdt.Elt, false)
520+
return checkAssignableTo(n, pt.Elt, cdt.Elt, false)
508521
}
509522
case *ArrayType:
510523
if at, ok := xt.(*ArrayType); ok {
@@ -526,7 +539,7 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
526539
case *SliceType:
527540
if st, ok := xt.(*SliceType); ok {
528541
if cdt.Vrd {
529-
return checkAssignableTo(st.Elt, cdt.Elt, false)
542+
return checkAssignableTo(n, st.Elt, cdt.Elt, false)
530543
} else {
531544
err := checkSame(st.Elt, cdt.Elt, "")
532545
if err != nil {
@@ -634,7 +647,7 @@ func (x *BinaryExpr) assertShiftExprCompatible1(store Store, last BlockNode, lt,
634647
if lt == UntypedBigdecType {
635648
// 1.0 << 1
636649
if lic && ric {
637-
convertConst(store, last, lcx, UntypedBigintType)
650+
convertConst(store, last, x, lcx, UntypedBigintType)
638651
return
639652
}
640653
}
@@ -697,11 +710,11 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
697710
case EQL, NEQ:
698711
assertComparable(xt, dt)
699712
if !isUntyped(xt) && !isUntyped(dt) {
700-
assertAssignableTo(xt, dt, false)
713+
assertAssignableTo(x, xt, dt, false)
701714
}
702715
case LSS, LEQ, GTR, GEQ:
703716
if checker, ok := binaryChecker[x.Op]; ok {
704-
x.checkCompatibility(xt, dt, checker, x.Op.TokenString())
717+
x.checkCompatibility(x, xt, dt, checker, x.Op.TokenString())
705718
} else {
706719
panic(fmt.Sprintf("checker for %s does not exist", x.Op))
707720
}
@@ -710,7 +723,7 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
710723
}
711724
} else {
712725
if checker, ok := binaryChecker[x.Op]; ok {
713-
x.checkCompatibility(xt, dt, checker, x.Op.TokenString())
726+
x.checkCompatibility(x, xt, dt, checker, x.Op.TokenString())
714727
} else {
715728
panic(fmt.Sprintf("checker for %s does not exist", x.Op))
716729
}
@@ -738,14 +751,14 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
738751
// The function checkOrConvertType will be invoked after this check.
739752
// NOTE: dt is established based on a specificity check between xt and dt,
740753
// confirming dt as the appropriate destination type for this context.
741-
func (x *BinaryExpr) checkCompatibility(xt, dt Type, checker func(t Type) bool, OpStr string) {
754+
func (x *BinaryExpr) checkCompatibility(n Node, xt, dt Type, checker func(t Type) bool, OpStr string) {
742755
if !checker(dt) {
743756
panic(fmt.Sprintf("operator %s not defined on: %v", OpStr, kindString(dt)))
744757
}
745758

746759
// if both typed
747760
if !isUntyped(xt) && !isUntyped(dt) {
748-
err := checkAssignableTo(xt, dt, false)
761+
err := checkAssignableTo(n, xt, dt, false)
749762
if err != nil {
750763
panic(fmt.Sprintf("invalid operation: mismatched types %v and %v", xt, dt))
751764
}
@@ -810,19 +823,19 @@ func (x *RangeStmt) AssertCompatible(store Store, last BlockNode) {
810823
xt := evalStaticTypeOf(store, last, x.X)
811824
switch cxt := xt.(type) {
812825
case *MapType:
813-
assertAssignableTo(cxt.Key, kt, false)
826+
assertAssignableTo(x, cxt.Key, kt, false)
814827
if vt != nil {
815-
assertAssignableTo(cxt.Value, vt, false)
828+
assertAssignableTo(x, cxt.Value, vt, false)
816829
}
817830
case *SliceType:
818831
assertIndexTypeIsInt(kt)
819832
if vt != nil {
820-
assertAssignableTo(cxt.Elt, vt, false)
833+
assertAssignableTo(x, cxt.Elt, vt, false)
821834
}
822835
case *ArrayType:
823836
assertIndexTypeIsInt(kt)
824837
if vt != nil {
825-
assertAssignableTo(cxt.Elt, vt, false)
838+
assertAssignableTo(x, cxt.Elt, vt, false)
826839
}
827840
case PrimitiveType:
828841
if cxt.Kind() == StringKind {
@@ -862,7 +875,7 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
862875
assertValidAssignLhs(store, last, lx)
863876
if !isBlankIdentifier(lx) {
864877
lxt := evalStaticTypeOf(store, last, lx)
865-
assertAssignableTo(cft.Results[i].Type, lxt, false)
878+
assertAssignableTo(x, cft.Results[i].Type, lxt, false)
866879
}
867880
}
868881
}
@@ -877,7 +890,7 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
877890
if !isBlankIdentifier(x.Lhs[0]) { // see composite3.gno
878891
dt := evalStaticTypeOf(store, last, x.Lhs[0])
879892
ift := evalStaticTypeOf(store, last, cx)
880-
assertAssignableTo(ift, dt, false)
893+
assertAssignableTo(x, ift, dt, false)
881894
}
882895
// check second value
883896
assertValidAssignLhs(store, last, x.Lhs[1])
@@ -900,12 +913,12 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
900913
if _, ok := cx.X.(*NameExpr); ok {
901914
rt := evalStaticTypeOf(store, last, cx.X)
902915
if mt, ok := rt.(*MapType); ok {
903-
assertAssignableTo(mt.Value, lt, false)
916+
assertAssignableTo(x, mt.Value, lt, false)
904917
}
905918
} else if _, ok := cx.X.(*CompositeLitExpr); ok {
906919
cpt := evalStaticTypeOf(store, last, cx.X)
907920
if mt, ok := cpt.(*MapType); ok {
908-
assertAssignableTo(mt.Value, lt, false)
921+
assertAssignableTo(x, mt.Value, lt, false)
909922
} else {
910923
panic("should not happen")
911924
}

‎gnovm/pkg/gnolang/type_check_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestCheckAssignableTo(t *testing.T) {
5151
}
5252
}()
5353
}
54-
checkAssignableTo(tt.xt, tt.dt, tt.autoNative)
54+
checkAssignableTo(nil, tt.xt, tt.dt, tt.autoNative)
5555
})
5656
}
5757
}

‎gnovm/pkg/gnolang/types.go

+19-19
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ func (ft *FuncType) UnboundType(rft FieldType) *FuncType {
11811181
// NOTE: if ft.HasVarg() and !isVarg, argTVs[len(ft.Params):]
11821182
// are ignored (since they are of the same type as
11831183
// argTVs[len(ft.Params)-1]).
1184-
func (ft *FuncType) Specify(store Store, argTVs []TypedValue, isVarg bool) *FuncType {
1184+
func (ft *FuncType) Specify(store Store, n Node, argTVs []TypedValue, isVarg bool) *FuncType {
11851185
hasGenericParams := false
11861186
hasGenericResults := false
11871187
for _, pf := range ft.Params {
@@ -1248,9 +1248,9 @@ func (ft *FuncType) Specify(store Store, argTVs []TypedValue, isVarg bool) *Func
12481248
for i, pf := range ft.Params {
12491249
arg := &argTVs[i]
12501250
if arg.T.Kind() == TypeKind {
1251-
specifyType(store, lookup, pf.Type, arg.T, arg.GetType())
1251+
specifyType(store, n, lookup, pf.Type, arg.T, arg.GetType())
12521252
} else {
1253-
specifyType(store, lookup, pf.Type, arg.T, nil)
1253+
specifyType(store, n, lookup, pf.Type, arg.T, nil)
12541254
}
12551255
}
12561256
// apply specifics to generic params and results.
@@ -2427,7 +2427,7 @@ func IsImplementedBy(it Type, ot Type) bool {
24272427
// specTypeval is Type if spec is TypeKind.
24282428
// NOTE: type-checking isn't strictly necessary here, as the resulting lookup
24292429
// map gets applied to produce the ultimate param and result types.
2430-
func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTypeval Type) {
2430+
func specifyType(store Store, n Node, lookup map[Name]Type, tmpl Type, spec Type, specTypeval Type) {
24312431
if isGeneric(spec) {
24322432
panic("spec must not be generic")
24332433
}
@@ -2441,11 +2441,11 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
24412441
case *PointerType:
24422442
switch pt := baseOf(spec).(type) {
24432443
case *PointerType:
2444-
specifyType(store, lookup, ct.Elt, pt.Elt, nil)
2444+
specifyType(store, n, lookup, ct.Elt, pt.Elt, nil)
24452445
case *NativeType:
24462446
// NOTE: see note about type-checking.
24472447
et := pt.Elem()
2448-
specifyType(store, lookup, ct.Elt, et, nil)
2448+
specifyType(store, n, lookup, ct.Elt, et, nil)
24492449
default:
24502450
panic(fmt.Sprintf(
24512451
"expected pointer kind but got %s",
@@ -2454,11 +2454,11 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
24542454
case *ArrayType:
24552455
switch at := baseOf(spec).(type) {
24562456
case *ArrayType:
2457-
specifyType(store, lookup, ct.Elt, at.Elt, nil)
2457+
specifyType(store, n, lookup, ct.Elt, at.Elt, nil)
24582458
case *NativeType:
24592459
// NOTE: see note about type-checking.
24602460
et := at.Elem()
2461-
specifyType(store, lookup, ct.Elt, et, nil)
2461+
specifyType(store, n, lookup, ct.Elt, et, nil)
24622462
default:
24632463
panic(fmt.Sprintf(
24642464
"expected array kind but got %s",
@@ -2469,7 +2469,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
24692469
case PrimitiveType:
24702470
if isGeneric(ct.Elt) {
24712471
if st.Kind() == StringKind {
2472-
specifyType(store, lookup, ct.Elt, Uint8Type, nil)
2472+
specifyType(store, n, lookup, ct.Elt, Uint8Type, nil)
24732473
} else {
24742474
panic(fmt.Sprintf(
24752475
"expected slice kind but got %s",
@@ -2485,11 +2485,11 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
24852485
spec.Kind()))
24862486
}
24872487
case *SliceType:
2488-
specifyType(store, lookup, ct.Elt, st.Elt, nil)
2488+
specifyType(store, n, lookup, ct.Elt, st.Elt, nil)
24892489
case *NativeType:
24902490
// NOTE: see note about type-checking.
24912491
et := st.Elem()
2492-
specifyType(store, lookup, ct.Elt, et, nil)
2492+
specifyType(store, n, lookup, ct.Elt, et, nil)
24932493
default:
24942494
panic(fmt.Sprintf(
24952495
"expected slice kind but got %s",
@@ -2498,14 +2498,14 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
24982498
case *MapType:
24992499
switch mt := baseOf(spec).(type) {
25002500
case *MapType:
2501-
specifyType(store, lookup, ct.Key, mt.Key, nil)
2502-
specifyType(store, lookup, ct.Value, mt.Value, nil)
2501+
specifyType(store, n, lookup, ct.Key, mt.Key, nil)
2502+
specifyType(store, n, lookup, ct.Value, mt.Value, nil)
25032503
case *NativeType:
25042504
// NOTE: see note about type-checking.
25052505
kt := mt.Key()
25062506
vt := mt.Elem()
2507-
specifyType(store, lookup, ct.Key, kt, nil)
2508-
specifyType(store, lookup, ct.Value, vt, nil)
2507+
specifyType(store, n, lookup, ct.Key, kt, nil)
2508+
specifyType(store, n, lookup, ct.Value, vt, nil)
25092509
default:
25102510
panic(fmt.Sprintf(
25112511
"expected map kind but got %s",
@@ -2544,7 +2544,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
25442544
generic := ct.Generic[:len(ct.Generic)-len(".Elem()")]
25452545
match, ok := lookup[generic]
25462546
if ok {
2547-
assertAssignableTo(spec, match.Elem(), false)
2547+
assertAssignableTo(n, spec, match.Elem(), false)
25482548
return // ok
25492549
} else {
25502550
// Panic here, because we don't know whether T
@@ -2558,7 +2558,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
25582558
} else {
25592559
match, ok := lookup[ct.Generic]
25602560
if ok {
2561-
assertAssignableTo(spec, match, false)
2561+
assertAssignableTo(n, spec, match, false)
25622562
return // ok
25632563
} else {
25642564
if isUntyped(spec) {
@@ -2576,9 +2576,9 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
25762576
switch cbt := baseOf(spec).(type) {
25772577
case *NativeType:
25782578
gnoType := store.Go2GnoType(cbt.Type)
2579-
specifyType(store, lookup, ct.Type, gnoType, nil)
2579+
specifyType(store, n, lookup, ct.Type, gnoType, nil)
25802580
default:
2581-
specifyType(store, lookup, ct.Type, cbt, nil)
2581+
specifyType(store, n, lookup, ct.Type, cbt, nil)
25822582
}
25832583
default:
25842584
// ignore, no generics.

‎gnovm/tests/files/add3.gno

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
func main() {
4+
a := 1
5+
i := a + nil
6+
}
7+
8+
// Error:
9+
// main/files/add3.gno:5:7: invalid operation: a<VPBlock(1,0)> + (const (undefined)) (mismatched types int and untyped nil)

‎gnovm/tests/files/assign38.gno

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
func main() {
4+
a := 1
5+
a = nil
6+
println(a)
7+
}
8+
9+
// Error:
10+
// main/files/assign38.gno:5:2: cannot use nil as int value in assignment

‎gnovm/tests/files/fun28.gno

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
func f(i int) {}
4+
5+
func main() {
6+
f(nil)
7+
}
8+
9+
// Error:
10+
// main/files/fun28.gno:6:2: cannot use nil as int value in argument to f<VPBlock(3,0)>

‎gnovm/tests/files/slice3.gno

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
func main() {
4+
i := []string{nil}
5+
println(i)
6+
}
7+
8+
// Error:
9+
// main/files/slice3.gno:4:7: cannot use nil as string value in array, slice literal or map literal

‎gnovm/tests/files/var35.gno

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package main
2+
3+
func main() {
4+
var i int = nil
5+
}
6+
7+
// Error:
8+
// main/files/var35.gno:4:6: cannot use nil as int value in variable declaration

0 commit comments

Comments
 (0)
Please sign in to comment.