From c8462ec305096ce483b455327bba077fa7ffe9f6 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 26 Aug 2024 17:20:10 -0700 Subject: [PATCH 01/40] preprocessor addressability work --- gnovm/pkg/gnolang/nodes.go | 122 +++++++++++++++++++++++++++++--- gnovm/pkg/gnolang/preprocess.go | 24 +++++++ 2 files changed, 137 insertions(+), 9 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index b18ed157ca6..fa29c225458 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -328,6 +328,7 @@ var ( type Expr interface { Node assertExpr() + isAddressable() bool } type Exprs []Expr @@ -374,6 +375,10 @@ type NameExpr struct { Name } +func (x *NameExpr) isAddressable() bool { + return true +} + type NameExprs []NameExpr type BasicLitExpr struct { @@ -385,6 +390,10 @@ type BasicLitExpr struct { Value string } +func (x *BasicLitExpr) isAddressable() bool { + return false +} + type BinaryExpr struct { // (Left Op Right) Attributes Left Expr // left operand @@ -392,12 +401,21 @@ type BinaryExpr struct { // (Left Op Right) Right Expr // right operand } +func (x *BinaryExpr) isAddressable() bool { + return false +} + type CallExpr struct { // Func(Args) Attributes - Func Expr // function expression - Args Exprs // function arguments, if any. - Varg bool // if true, final arg is variadic. - NumArgs int // len(Args) or len(Args[0].Results) + Func Expr // function expression + Args Exprs // function arguments, if any. + Varg bool // if true, final arg is variadic. + NumArgs int // len(Args) or len(Args[0].Results) + IsAddressable bool +} + +func (x *CallExpr) isAddressable() bool { + return x.IsAddressable } type IndexExpr struct { // X[Index] @@ -407,11 +425,20 @@ type IndexExpr struct { // X[Index] HasOK bool // if true, is form: `value, ok := [] } +func (x *IndexExpr) isAddressable() bool { + return x.X.isAddressable() +} + type SelectorExpr struct { // X.Sel Attributes - X Expr // expression - Path ValuePath // set by preprocessor. - Sel Name // field selector + X Expr // expression + Path ValuePath // set by preprocessor. + Sel Name // field selector + IsAddressable bool +} + +func (x *SelectorExpr) isAddressable() bool { + return x.IsAddressable } type SliceExpr struct { // X[Low:High:Max] @@ -422,6 +449,10 @@ type SliceExpr struct { // X[Low:High:Max] Max Expr // maximum capacity of slice; or nil; added in Go 1.2 } +func (x *SliceExpr) isAddressable() bool { + return x.X.isAddressable() +} + // A StarExpr node represents an expression of the form // "*" Expression. Semantically it could be a unary "*" // expression, or a pointer type. @@ -430,11 +461,19 @@ type StarExpr struct { // *X X Expr // operand } +func (x *StarExpr) isAddressable() bool { + return false +} + type RefExpr struct { // &X Attributes X Expr // operand } +func (x *RefExpr) isAddressable() bool { + return x.X.isAddressable() +} + type TypeAssertExpr struct { // X.(Type) Attributes X Expr // expression. @@ -442,6 +481,10 @@ type TypeAssertExpr struct { // X.(Type) HasOK bool // if true, is form: `_, ok := .()`. } +func (x *TypeAssertExpr) isAddressable() bool { + return x.X.isAddressable() +} + // A UnaryExpr node represents a unary expression. Unary // "*" expressions (dereferencing and pointer-types) are // represented with StarExpr nodes. Unary & expressions @@ -452,12 +495,21 @@ type UnaryExpr struct { // (Op X) Op Word // operator } +func (x *UnaryExpr) isAddressable() bool { + return x.X.isAddressable() +} + // MyType{:} struct, array, slice, and map // expressions. type CompositeLitExpr struct { Attributes - Type Expr // literal type; or nil - Elts KeyValueExprs // list of struct fields; if any + Type Expr // literal type; or nil + Elts KeyValueExprs // list of struct fields; if any + IsAddressable bool +} + +func (x *CompositeLitExpr) isAddressable() bool { + return x.IsAddressable } // Returns true if any elements are keyed. @@ -490,6 +542,10 @@ type KeyValueExpr struct { Value Expr // never nil } +func (x *KeyValueExpr) isAddressable() bool { + return false +} + type KeyValueExprs []KeyValueExpr // A FuncLitExpr node represents a function literal. Here one @@ -502,6 +558,10 @@ type FuncLitExpr struct { Body // function body } +func (x *FuncLitExpr) isAddressable() bool { + return false +} + // The preprocessor replaces const expressions // with *ConstExpr nodes. type ConstExpr struct { @@ -510,6 +570,10 @@ type ConstExpr struct { TypedValue } +func (x *ConstExpr) isAddressable() bool { + return false +} + // ---------------------------------------- // Type(Expressions) // @@ -574,6 +638,10 @@ type FieldTypeExpr struct { Tag Expr } +func (x *FieldTypeExpr) isAddressable() bool { + return false +} + type FieldTypeExprs []FieldTypeExpr func (ftxz FieldTypeExprs) IsNamed() bool { @@ -598,18 +666,30 @@ type ArrayTypeExpr struct { Elt Expr // element type } +func (x *ArrayTypeExpr) isAddressable() bool { + return false +} + type SliceTypeExpr struct { Attributes Elt Expr // element type Vrd bool // variadic arg expression } +func (x *SliceTypeExpr) isAddressable() bool { + return false +} + type InterfaceTypeExpr struct { Attributes Methods FieldTypeExprs // list of methods Generic Name // for uverse generics } +func (x *InterfaceTypeExpr) isAddressable() bool { + return false +} + type ChanDir int const ( @@ -627,23 +707,39 @@ type ChanTypeExpr struct { Value Expr // value type } +func (x *ChanTypeExpr) isAddressable() bool { + return false +} + type FuncTypeExpr struct { Attributes Params FieldTypeExprs // (incoming) parameters, if any. Results FieldTypeExprs // (outgoing) results, if any. } +func (x *FuncTypeExpr) isAddressable() bool { + return false +} + type MapTypeExpr struct { Attributes Key Expr // const Value Expr // value type } +func (x *MapTypeExpr) isAddressable() bool { + return false +} + type StructTypeExpr struct { Attributes Fields FieldTypeExprs // list of field declarations } +func (x *StructTypeExpr) isAddressable() bool { + return false +} + // Like ConstExpr but for types. type constTypeExpr struct { Attributes @@ -651,12 +747,20 @@ type constTypeExpr struct { Type Type } +func (x *constTypeExpr) isAddressable() bool { + return false +} + // Only used for native func arguments type MaybeNativeTypeExpr struct { Attributes Type Expr } +func (x *MaybeNativeTypeExpr) isAddressable() bool { + return false +} + // ---------------------------------------- // Stmt // diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index ba60ead28f6..49382a46511 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1341,6 +1341,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } + if len(ft.Results) == 1 { + switch ft.Results[0].Type.(type) { + case *PointerType, *InterfaceType: + n.IsAddressable = true + } + } + // Continue with general case. hasVarg := ft.HasVarg() isVarg := n.Varg @@ -1603,6 +1610,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { convertType(store, last, &n.Elts[i].Key, IntType) checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false) } + + // Slices are always addressable because the underlying array + // is added to the heap during initialization. + n.IsAddressable = true case *MapType: for i := 0; i < len(n.Elts); i++ { checkOrConvertType(store, last, &n.Elts[i].Key, cclt.Key, false) @@ -1642,6 +1653,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } + if ftype == TRANS_REF_X { + n.IsAddressable = true + } + // TRANS_LEAVE ----------------------- case *KeyValueExpr: // NOTE: For simplicity we just @@ -1650,6 +1665,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *SelectorExpr: xt := evalStaticTypeOf(store, last, n.X) + switch xt.(type) { + case *PointerType, *InterfaceType: + n.IsAddressable = true + } // Set selector path based on xt's type. switch cxt := xt.(type) { @@ -2360,6 +2379,11 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Replace the type with *constTypeExpr{}, // otherwise methods would be un at runtime. n.Type = constType(n.Type, dst) + + case *RefExpr: + if !n.X.isAddressable() { + panic(fmt.Sprintf("cannot take address of %s", n.X.String())) + } } // end type switch statement // END TRANS_LEAVE ----------------------- From ef972744bb9a52354869085854bbe97b0b8f0be1 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 26 Aug 2024 17:40:51 -0700 Subject: [PATCH 02/40] fix selector isaddressable --- gnovm/pkg/gnolang/nodes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index fa29c225458..48d778c59a8 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -438,7 +438,7 @@ type SelectorExpr struct { // X.Sel } func (x *SelectorExpr) isAddressable() bool { - return x.IsAddressable + return x.IsAddressable || x.X.isAddressable() } type SliceExpr struct { // X[Low:High:Max] From 7fa9f501b825e1f4dd3f88aa4dbd125389ad1a36 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 26 Aug 2024 17:48:02 -0700 Subject: [PATCH 03/40] fix starexpr isaddressable --- gnovm/pkg/gnolang/nodes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 48d778c59a8..1de435a1971 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -462,7 +462,7 @@ type StarExpr struct { // *X } func (x *StarExpr) isAddressable() bool { - return false + return x.X.isAddressable() } type RefExpr struct { // &X From 08563c3967632e691c920ae44258c369bac2e7f4 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 09:45:30 -0700 Subject: [PATCH 04/40] fix sliceexpr addressability --- gnovm/pkg/gnolang/nodes.go | 11 ++++++----- gnovm/pkg/gnolang/preprocess.go | 17 ++++++++++++++++- gnovm/stdlibs/crypto/sha256/sha256_test.gno | 3 ++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 1de435a1971..66691fecb0c 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -443,14 +443,15 @@ func (x *SelectorExpr) isAddressable() bool { type SliceExpr struct { // X[Low:High:Max] Attributes - X Expr // expression - Low Expr // begin of slice range; or nil - High Expr // end of slice range; or nil - Max Expr // maximum capacity of slice; or nil; added in Go 1.2 + X Expr // expression + Low Expr // begin of slice range; or nil + High Expr // end of slice range; or nil + Max Expr // maximum capacity of slice; or nil; added in Go 1.2 + IsAddressable bool } func (x *SliceExpr) isAddressable() bool { - return x.X.isAddressable() + return x.IsAddressable } // A StarExpr node represents an expression of the form diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 49382a46511..fdb01afddf1 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1510,8 +1510,23 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { checkOrConvertIntegerKind(store, last, n.High) checkOrConvertIntegerKind(store, last, n.Max) - // if n.X is untyped, convert to corresponding type t := evalStaticTypeOf(store, last, n.X) + n.IsAddressable = true + + // Non-pointer arrays and strings produced by the slice expression are not addressable. + // Slices and pointers are addressable. + switch t.(type) { + case *ArrayType: + if !n.X.isAddressable() { + panic(fmt.Sprintf("cannot take address of %s", n.X.String())) + } + + n.IsAddressable = false + case *PrimitiveType: + n.IsAddressable = false + } + + // if n.X is untyped, convert to corresponding type if isUntyped(t) { dt := defaultTypeOf(t) checkOrConvertType(store, last, &n.X, dt, false) diff --git a/gnovm/stdlibs/crypto/sha256/sha256_test.gno b/gnovm/stdlibs/crypto/sha256/sha256_test.gno index 26d96cd547e..809f826f007 100644 --- a/gnovm/stdlibs/crypto/sha256/sha256_test.gno +++ b/gnovm/stdlibs/crypto/sha256/sha256_test.gno @@ -7,7 +7,8 @@ import ( ) func TestSha256Sum(t *testing.T) { - got := sha256.Sum256([]byte("sha256 this string"))[:] + result := sha256.Sum256([]byte("sha256 this string")) + got := result[:] expected := "1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a0d3db739d77aacb" if hex.EncodeToString(got) != expected { From 3b78ae921fed82601601ab293ba3310a7f31fe51 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 10:11:06 -0700 Subject: [PATCH 05/40] fix typeassertexpr addressability --- gnovm/pkg/gnolang/nodes.go | 9 +++++---- gnovm/pkg/gnolang/preprocess.go | 7 ++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 66691fecb0c..201d78696c4 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -477,13 +477,14 @@ func (x *RefExpr) isAddressable() bool { type TypeAssertExpr struct { // X.(Type) Attributes - X Expr // expression. - Type Expr // asserted type, never nil. - HasOK bool // if true, is form: `_, ok := .()`. + X Expr // expression. + Type Expr // asserted type, never nil. + HasOK bool // if true, is form: `_, ok := .()`. + IsAddressable bool } func (x *TypeAssertExpr) isAddressable() bool { - return x.X.isAddressable() + return x.IsAddressable } // A UnaryExpr node represents a unary expression. Unary diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index fdb01afddf1..fe8228588f1 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1564,7 +1564,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ) } - evalStaticType(store, last, n.Type) + if _, ok := evalStaticType(store, last, n.Type).(*PointerType); ok { + // If the type assertion is to a pointer type, the result is addressable. + // Type assertions for other types result in a copy of the value or an + // interface, both non-addressable. + n.IsAddressable = true + } // TRANS_LEAVE ----------------------- case *UnaryExpr: From a0e13b9d7031f07cfaf061c12f1586cf19bcb014 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 13:21:47 -0700 Subject: [PATCH 06/40] fix callexpr addressable return types --- gnovm/pkg/gnolang/preprocess.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index fe8228588f1..d5983eb7161 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1343,7 +1343,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if len(ft.Results) == 1 { switch ft.Results[0].Type.(type) { - case *PointerType, *InterfaceType: + case *PointerType, *SliceType: n.IsAddressable = true } } From ae55b84abf0b7dfac2f98c58213209f32695f33b Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 13:38:24 -0700 Subject: [PATCH 07/40] mark append and new results as addressable --- gnovm/pkg/gnolang/preprocess.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index d5983eb7161..6db26a64cf2 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1292,6 +1292,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if cx, ok := n.Func.(*ConstExpr); ok { fv := cx.GetFunc() if fv.PkgPath == uversePkgPath && fv.Name == "append" { + // append returns a slice and slices are always addressable. + n.IsAddressable = true if n.Varg && len(n.Args) == 2 { // If the second argument is a string, // convert to byteslice. @@ -1338,10 +1340,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Args[1] = args1 } } + } else if fv.PkgPath == uversePkgPath && fv.Name == "new" { + // new returns a pointer and pointers are always addressable. + n.IsAddressable = true } } - if len(ft.Results) == 1 { + if !n.IsAddressable && len(ft.Results) == 1 { switch ft.Results[0].Type.(type) { case *PointerType, *SliceType: n.IsAddressable = true From 990017d1378508752fe7167370fb2558fec9aeaf Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 15:36:11 -0700 Subject: [PATCH 08/40] fix selector addressability --- gnovm/pkg/gnolang/preprocess.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 6db26a64cf2..560322ce6ff 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1690,15 +1690,14 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *SelectorExpr: xt := evalStaticTypeOf(store, last, n.X) - switch xt.(type) { - case *PointerType, *InterfaceType: + if xt.Kind() == PointerKind { n.IsAddressable = true } // Set selector path based on xt's type. switch cxt := xt.(type) { case *PointerType, *DeclaredType, *StructType, *InterfaceType: - tr, _, rcvr, _, aerr := findEmbeddedFieldType(lastpn.PkgPath, cxt, n.Sel, nil) + tr, _, rcvr, fieldType, aerr := findEmbeddedFieldType(lastpn.PkgPath, cxt, n.Sel, nil) if aerr { panic(fmt.Sprintf("cannot access %s.%s from %s", cxt.String(), n.Sel, lastpn.PkgPath)) @@ -1706,6 +1705,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { panic(fmt.Sprintf("missing field %s in %s", n.Sel, cxt.String())) } + + // In the case where n.X is a non-pointer declared type or struct, + // only selectors of pointers or slices are addressable. Strings + // are not included in this check, but will be marked as addressable + // during the TRANS_LEAVE of SliceExpr if this is selecting a string. + if fieldType.Kind() == PointerKind || fieldType.Kind() == SliceKind { + n.IsAddressable = true + } + if len(tr) > 1 { // (the last vp, tr[len(tr)-1], is for n.Sel) if debug { From 3621dc76e38ec50e9e4b4537d4c987724ccc4c95 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 17:34:23 -0700 Subject: [PATCH 09/40] make string indexes non-addressable --- gnovm/pkg/gnolang/nodes.go | 9 +++++---- gnovm/pkg/gnolang/preprocess.go | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 201d78696c4..f1f25ca4419 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -420,13 +420,14 @@ func (x *CallExpr) isAddressable() bool { type IndexExpr struct { // X[Index] Attributes - X Expr // expression - Index Expr // index expression - HasOK bool // if true, is form: `value, ok := [] + X Expr // expression + Index Expr // index expression + HasOK bool // if true, is form: `value, ok := [] + NotAddressable bool } func (x *IndexExpr) isAddressable() bool { - return x.X.isAddressable() + return !x.NotAddressable && x.X.isAddressable() } type SelectorExpr struct { // X.Sel diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 560322ce6ff..e00f5286565 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1498,6 +1498,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Replace const index with int *ConstExpr, // or if not const, assert integer type.. checkOrConvertIntegerKind(store, last, n.Index) + if dt.Kind() == StringKind { + // A string index is not addressable. + n.NotAddressable = true + } case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) checkOrConvertType(store, last, &n.Index, mt.Key, false) From 9faf3dfacf055d03e18bc5526b3491ad90be2219 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Aug 2024 17:34:55 -0700 Subject: [PATCH 10/40] addressability file tests --- gnovm/tests/dylan/addressable_1.gno | 38 ++++++++ gnovm/tests/dylan/addressable_1a_err.gno | 8 ++ gnovm/tests/dylan/addressable_1b_err.gno | 8 ++ gnovm/tests/dylan/addressable_1c_err.gno | 13 +++ gnovm/tests/dylan/addressable_1d_err.gno | 12 +++ gnovm/tests/dylan/addressable_2.gno | 40 +++++++++ gnovm/tests/dylan/addressable_2a_err.gno | 8 ++ gnovm/tests/dylan/addressable_3.gno | 105 +++++++++++++++++++++++ gnovm/tests/dylan/addressable_3a_err.gno | 14 +++ gnovm/tests/dylan/addressable_3b_err.gno | 16 ++++ gnovm/tests/dylan/addressable_4.gno | 23 +++++ gnovm/tests/dylan/addressable_4a_err.gno | 9 ++ 12 files changed, 294 insertions(+) create mode 100644 gnovm/tests/dylan/addressable_1.gno create mode 100644 gnovm/tests/dylan/addressable_1a_err.gno create mode 100644 gnovm/tests/dylan/addressable_1b_err.gno create mode 100644 gnovm/tests/dylan/addressable_1c_err.gno create mode 100644 gnovm/tests/dylan/addressable_1d_err.gno create mode 100644 gnovm/tests/dylan/addressable_2.gno create mode 100644 gnovm/tests/dylan/addressable_2a_err.gno create mode 100644 gnovm/tests/dylan/addressable_3.gno create mode 100644 gnovm/tests/dylan/addressable_3a_err.gno create mode 100644 gnovm/tests/dylan/addressable_3b_err.gno create mode 100644 gnovm/tests/dylan/addressable_4.gno create mode 100644 gnovm/tests/dylan/addressable_4a_err.gno diff --git a/gnovm/tests/dylan/addressable_1.gno b/gnovm/tests/dylan/addressable_1.gno new file mode 100644 index 00000000000..30616145733 --- /dev/null +++ b/gnovm/tests/dylan/addressable_1.gno @@ -0,0 +1,38 @@ +package main + +func main() { + // Array pointers are addressable. + println(&getArrPtr1()[0]) + println(&getArrPtr2()[0]) + println(&getArrPtr3()[0]) + println(&new([1]int)[0]) + + // Array pointers are slicable. + println(getArrPtr1()[:]) + println(getArrPtr2()[:]) + println(getArrPtr3()[:]) + println(new([1]int)[:]) +} + +func getArrPtr1() *[1]int { + return &[1]int{1} +} + +func getArrPtr2() *[1]int { + a := [1]int{2} + return &a +} + +func getArrPtr3() *[1]int { + return new([1]int) +} + +// Output: +// &(1 int) +// &(2 int) +// &(0 int) +// &(0 int) +// slice[(1 int)] +// slice[(2 int)] +// slice[(0 int)] +// slice[(0 int)] diff --git a/gnovm/tests/dylan/addressable_1a_err.gno b/gnovm/tests/dylan/addressable_1a_err.gno new file mode 100644 index 00000000000..e6a9da10048 --- /dev/null +++ b/gnovm/tests/dylan/addressable_1a_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = &[1]int{1}[0] +} + +// Error: +// main/dylan/addressable_1a_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))}[(const (0 int))] diff --git a/gnovm/tests/dylan/addressable_1b_err.gno b/gnovm/tests/dylan/addressable_1b_err.gno new file mode 100644 index 00000000000..038d1dd3c26 --- /dev/null +++ b/gnovm/tests/dylan/addressable_1b_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = [1]int{1}[:] +} + +// Error: +// main/dylan/addressable_1b_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))} diff --git a/gnovm/tests/dylan/addressable_1c_err.gno b/gnovm/tests/dylan/addressable_1c_err.gno new file mode 100644 index 00000000000..ada7146a9a1 --- /dev/null +++ b/gnovm/tests/dylan/addressable_1c_err.gno @@ -0,0 +1,13 @@ +package main + +func main() { + _ = &getArr()[0] +} + +func getArr() [1]int { + arr := [1]int{1} + return arr +} + +// Error: +// main/dylan/addressable_1c_err.gno:4:6: cannot take address of getArr()[(const (0 int))] diff --git a/gnovm/tests/dylan/addressable_1d_err.gno b/gnovm/tests/dylan/addressable_1d_err.gno new file mode 100644 index 00000000000..57dab7c35f3 --- /dev/null +++ b/gnovm/tests/dylan/addressable_1d_err.gno @@ -0,0 +1,12 @@ +package main + +func main() { + _ = getArr()[:] +} + +func getArr() [1]int { + return [1]int{1} +} + +// Error: +// main/dylan/addressable_1d_err.gno:4:6: cannot take address of getArr() diff --git a/gnovm/tests/dylan/addressable_2.gno b/gnovm/tests/dylan/addressable_2.gno new file mode 100644 index 00000000000..1baf7f29e65 --- /dev/null +++ b/gnovm/tests/dylan/addressable_2.gno @@ -0,0 +1,40 @@ +package main + +func main() { + // Slices are addressable because the underlying array is addressable + // after slice initialization. + println(&[]int{1}[0]) + println(&getSlice1()[0]) + println(&getSlice2()[0]) + println(&[]int{1}) + + a := []int{1} + println(&append(a, 1, 2, 3, 4, 5)[5]) + + println([]int{1}[:]) + println(getSlice1()[:]) + println(getSlice2()[:]) + + b := []int{1} + println(append(b, 1, 2, 3, 4, 5)[:]) +} + +func getSlice1() []int { + return []int{2} +} + +func getSlice2() []int { + s := []int{3} + return s +} + +// Output: +// &(1 int) +// &(2 int) +// &(3 int) +// &(slice[(1 int)] []int) +// &(5 int) +// slice[(1 int)] +// slice[(2 int)] +// slice[(3 int)] +// slice[(1 int),(1 int),(2 int),(3 int),(4 int),(5 int)] diff --git a/gnovm/tests/dylan/addressable_2a_err.gno b/gnovm/tests/dylan/addressable_2a_err.gno new file mode 100644 index 00000000000..42b04868f13 --- /dev/null +++ b/gnovm/tests/dylan/addressable_2a_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = &[]int{1}[:] +} + +// Error: +// illegal assignment X expression type *gnolang.SliceExpr diff --git a/gnovm/tests/dylan/addressable_3.gno b/gnovm/tests/dylan/addressable_3.gno new file mode 100644 index 00000000000..1a955b97fe7 --- /dev/null +++ b/gnovm/tests/dylan/addressable_3.gno @@ -0,0 +1,105 @@ +package main + +type S struct { + i int + ip *int + a [1]int + ap *[1]int + s []int +} + +func main() { + intPtr := new(int) + *intPtr = 9 + + v1 := S{ + i: 4, + ip: intPtr, + a: [1]int{5}, + ap: new([1]int), + s: []int{6}, + } + + // v1 and all members are addressable. + println(&v1) + println(&v1.i) + println(*v1.ip) + println(&v1.a[0]) + println(&v1.ap[0]) + println(&v1.s[0]) + println("") + + // Defining a struct as a pointer also makes a member addressable. + println(&(&S{i: 4}).i) + println("") + + // Print only the members that are addressable when S is not addressable. + println(*S{ip: intPtr}.ip) + println(&S{ap: new([1]int)}.ap[0]) + println(&S{s: []int{6}}.s[0]) + println("") + + // A struct value returned by a function is not addressable. + // Only certain members are addressable. + println(*getStruct().ip) + println(&getStruct().ap[0]) + println(&getStruct().s[0]) + println("") + + // A struct pointer value returned by a function has all members addressable. + println(&getStructPtr().i) + println(*getStructPtr().ip) + println(&getStructPtr().a[0]) + println(&getStructPtr().ap[0]) + println(&getStructPtr().s[0]) +} + +func getStruct() S { + intPtr := new(int) + *intPtr = 9 + + return S{ + i: 4, + ip: intPtr, + a: [1]int{5}, + ap: new([1]int), + s: []int{6}, + } +} + +func getStructPtr() *S { + intPtr := new(int) + *intPtr = 9 + + return &S{ + i: 4, + ip: intPtr, + a: [1]int{5}, + ap: new([1]int), + s: []int{6}, + } +} + +// Output: +// &(struct{(4 int),(&(9 int) *int),(array[(5 int)] [1]int),(&(array[(0 int)] [1]int) *[1]int),(slice[(6 int)] []int)} main.S) +// &(4 int) +// 9 +// &(5 int) +// &(0 int) +// &(6 int) +// +// &(4 int) +// +// 9 +// &(0 int) +// &(6 int) +// +// 9 +// &(0 int) +// &(6 int) +// +// &(4 int) +// 9 +// &(5 int) +// &(0 int) +// &(6 int) diff --git a/gnovm/tests/dylan/addressable_3a_err.gno b/gnovm/tests/dylan/addressable_3a_err.gno new file mode 100644 index 00000000000..2154f7e7bea --- /dev/null +++ b/gnovm/tests/dylan/addressable_3a_err.gno @@ -0,0 +1,14 @@ +package main + +type S struct { + i int +} + +func main() { + // Can't take the address of non-addressable member of + // a non-addressable struct. + _ = &S{i: 4}.i +} + +// Error: +// main/dylan/addressable_3a_err.gno:10:6: cannot take address of S{i: (const (4 int))}.i diff --git a/gnovm/tests/dylan/addressable_3b_err.gno b/gnovm/tests/dylan/addressable_3b_err.gno new file mode 100644 index 00000000000..c31a4692555 --- /dev/null +++ b/gnovm/tests/dylan/addressable_3b_err.gno @@ -0,0 +1,16 @@ +package main + +type S struct { + i int +} + +func main() { + _ = &getStruct().i +} + +func getStruct() S { + return S{i: 9} +} + +// Error: +// main/dylan/addressable_3b_err.gno:8:6: cannot take address of getStruct().i diff --git a/gnovm/tests/dylan/addressable_4.gno b/gnovm/tests/dylan/addressable_4.gno new file mode 100644 index 00000000000..322cf8eabb3 --- /dev/null +++ b/gnovm/tests/dylan/addressable_4.gno @@ -0,0 +1,23 @@ +package main + +type S struct { + s string +} + +func main() { + // Strings are special; they are slicable even if not addressable. + println("hello"[:]) + println("hello"[0]) + println("hello"[2:4]) + println(S{s: "hello"}.s[:]) + println(S{s: "hello"}.s[0]) + println(S{s: "hello"}.s[2:4]) +} + +// Output: +// hello +// 104 +// ll +// hello +// 104 +// ll diff --git a/gnovm/tests/dylan/addressable_4a_err.gno b/gnovm/tests/dylan/addressable_4a_err.gno new file mode 100644 index 00000000000..cceab42c600 --- /dev/null +++ b/gnovm/tests/dylan/addressable_4a_err.gno @@ -0,0 +1,9 @@ +package main + +func main() { + greeting := "hello" + _ = &greeting[2] +} + +// Error: +// main/dylan/addressable_4a_err.gno:5:6: cannot take address of greeting[(const (2 int))] From 8671ea591ee1d2e8b5f01d63428907478975f30b Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 28 Aug 2024 14:44:59 -0700 Subject: [PATCH 11/40] prohibit taking address of imported typed constant --- gnovm/pkg/gnolang/nodes.go | 12 ++++++++++++ gnovm/pkg/gnolang/preprocess.go | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index f1f25ca4419..bcc70aa85b4 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1591,6 +1591,7 @@ type BlockNode interface { GetParentNode(Store) BlockNode GetPathForName(Store, Name) ValuePath GetIsConst(Store, Name) bool + GetIsConstAt(Store, ValuePath) bool GetLocalIndex(Name) (uint16, bool) GetValueRef(Store, Name, bool) *TypedValue GetStaticTypeOf(Store, Name) Type @@ -1778,6 +1779,17 @@ func (sb *StaticBlock) GetIsConst(store Store, n Name) bool { } } +func (sb *StaticBlock) GetIsConstAt(store Store, path ValuePath) bool { + for { + if path.Depth == 1 { + return sb.getLocalIsConst(path.Name) + } else { + sb = sb.GetParentNode(store).GetStaticBlock() + path.Depth -= 1 + } + } +} + // Returns true iff n is a local const defined name. func (sb *StaticBlock) getLocalIsConst(n Name) bool { for _, name := range sb.Consts { diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e00f5286565..fab6683cf5b 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1820,10 +1820,11 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // packages may contain constant vars, // so check and evaluate if so. tt := pn.GetStaticTypeOfAt(store, n.Path) - if isUntyped(tt) { + if isUntyped(tt) || pn.GetIsConstAt(store, n.Path) { cx := evalConst(store, last, n) return cx, TRANS_CONTINUE } + case *TypeType: // unbound method xt := evalStaticType(store, last, n.X) From 816dbd6441a8c6e32cdf3b49f4f7589847b3f281 Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 28 Aug 2024 15:45:42 -0700 Subject: [PATCH 12/40] more tests --- gnovm/tests/dylan/addressable_5.gno | 11 +++++++++++ gnovm/tests/dylan/addressable_5a_err.gno | 11 +++++++++++ gnovm/tests/dylan/addressable_5b_err.gno | 11 +++++++++++ gnovm/tests/dylan/addressable_5c_err.gno | 10 ++++++++++ gnovm/tests/dylan/addressable_5d_err.gno | 10 ++++++++++ 5 files changed, 53 insertions(+) create mode 100644 gnovm/tests/dylan/addressable_5.gno create mode 100644 gnovm/tests/dylan/addressable_5a_err.gno create mode 100644 gnovm/tests/dylan/addressable_5b_err.gno create mode 100644 gnovm/tests/dylan/addressable_5c_err.gno create mode 100644 gnovm/tests/dylan/addressable_5d_err.gno diff --git a/gnovm/tests/dylan/addressable_5.gno b/gnovm/tests/dylan/addressable_5.gno new file mode 100644 index 00000000000..800cc744458 --- /dev/null +++ b/gnovm/tests/dylan/addressable_5.gno @@ -0,0 +1,11 @@ +package main + +import "encoding/binary" + +func main() { + // Verify that addressable results of expressions are + // still addressable when accessed via a selector. + var b []byte + le := &binary.LittleEndian + println(&le.AppendUint16(b, 0)[0]) +} diff --git a/gnovm/tests/dylan/addressable_5a_err.gno b/gnovm/tests/dylan/addressable_5a_err.gno new file mode 100644 index 00000000000..c41f1ee086f --- /dev/null +++ b/gnovm/tests/dylan/addressable_5a_err.gno @@ -0,0 +1,11 @@ +package main + +import "math" + +func main() { + // Untyped constants are not addressable. + _ = &math.MaxUint8 +} + +// Error: +// main/dylan/addressable_5a_err.gno:7:6: cannot take address of (const (255 bigint)) diff --git a/gnovm/tests/dylan/addressable_5b_err.gno b/gnovm/tests/dylan/addressable_5b_err.gno new file mode 100644 index 00000000000..12e0f121195 --- /dev/null +++ b/gnovm/tests/dylan/addressable_5b_err.gno @@ -0,0 +1,11 @@ +package main + +import "std" + +func main() { + // Type constants are not addressable. + _ = &std.BankerTypeReadonly +} + +// Error: +// main/dylan/addressable_5b_err.gno:7:6: cannot take address of (const (0 std.BankerType)) diff --git a/gnovm/tests/dylan/addressable_5c_err.gno b/gnovm/tests/dylan/addressable_5c_err.gno new file mode 100644 index 00000000000..f4489657db9 --- /dev/null +++ b/gnovm/tests/dylan/addressable_5c_err.gno @@ -0,0 +1,10 @@ +package main + +const a = 1 + +func main() { + _ = &a +} + +// Error: +// main/dylan/addressable_5c_err.gno:6:6: cannot take address of (const (1 bigint)) diff --git a/gnovm/tests/dylan/addressable_5d_err.gno b/gnovm/tests/dylan/addressable_5d_err.gno new file mode 100644 index 00000000000..9d2c4ba1b78 --- /dev/null +++ b/gnovm/tests/dylan/addressable_5d_err.gno @@ -0,0 +1,10 @@ +package main + +const a int = 1 + +func main() { + _ = &a +} + +// Error: +// main/dylan/addressable_5d_err.gno:6:6: cannot take address of (const (1 int)) From 77caea9da0ec8217961399bf80053e4b7fd6787f Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 28 Aug 2024 20:45:53 -0700 Subject: [PATCH 13/40] fix type assertion addressability and add tests --- gnovm/pkg/gnolang/preprocess.go | 9 +++++--- gnovm/tests/dylan/addressable_6.gno | 27 ++++++++++++++++++++++++ gnovm/tests/dylan/addressable_6a_err.gno | 10 +++++++++ gnovm/tests/dylan/addressable_6b_err.gno | 14 ++++++++++++ gnovm/tests/dylan/addressable_6c_err.gno | 10 +++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 gnovm/tests/dylan/addressable_6.gno create mode 100644 gnovm/tests/dylan/addressable_6a_err.gno create mode 100644 gnovm/tests/dylan/addressable_6b_err.gno create mode 100644 gnovm/tests/dylan/addressable_6c_err.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index fab6683cf5b..e6693fc1cae 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1573,10 +1573,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ) } - if _, ok := evalStaticType(store, last, n.Type).(*PointerType); ok { - // If the type assertion is to a pointer type, the result is addressable. + targetType := baseOf(evalStaticType(store, last, n.Type)) + switch targetType.(type) { + case *PointerType, *SliceType: + // If the type assertion is to a pointer of slice, the result is addressable. // Type assertions for other types result in a copy of the value or an - // interface, both non-addressable. + // interface, both non-addressable. Slices are addressable because the copy + // of the slice still references the underlying array. n.IsAddressable = true } diff --git a/gnovm/tests/dylan/addressable_6.gno b/gnovm/tests/dylan/addressable_6.gno new file mode 100644 index 00000000000..9fc0616f980 --- /dev/null +++ b/gnovm/tests/dylan/addressable_6.gno @@ -0,0 +1,27 @@ +package main + +type S struct { + a int +} + +type Alias []int + +func main() { + // Type assertions copy the value being asserted, so only pointers and + // slices are addressable. Slices are addressable because a copy of a slice + // maintains a reference to the same underlying array. + var i interface{} + i = []int{1} + println(&i.([]int)[0]) + + i = &S{} + println(&i.(*S).a) + + i = Alias{4} + println(&i.(Alias)[0]) +} + +// Output: +// &(1 int) +// &(0 int) +// &(4 int) diff --git a/gnovm/tests/dylan/addressable_6a_err.gno b/gnovm/tests/dylan/addressable_6a_err.gno new file mode 100644 index 00000000000..8d4f2866f78 --- /dev/null +++ b/gnovm/tests/dylan/addressable_6a_err.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var i interface{} + i = 5 + println(&i.(int)) +} + +// Error: +// main/dylan/addressable_6a_err.gno:6:10: cannot take address of i.((const-type int)) diff --git a/gnovm/tests/dylan/addressable_6b_err.gno b/gnovm/tests/dylan/addressable_6b_err.gno new file mode 100644 index 00000000000..6c9b7d449d5 --- /dev/null +++ b/gnovm/tests/dylan/addressable_6b_err.gno @@ -0,0 +1,14 @@ +package main + +type S struct { + a int +} + +func main() { + var i interface{} + i = S{a: 9} + println(&i.(S).a) +} + +// Error: +// main/dylan/addressable_6b_err.gno:10:10: cannot take address of i.(S).a diff --git a/gnovm/tests/dylan/addressable_6c_err.gno b/gnovm/tests/dylan/addressable_6c_err.gno new file mode 100644 index 00000000000..9837999eff1 --- /dev/null +++ b/gnovm/tests/dylan/addressable_6c_err.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var i interface{} + i = [1]int{1} + println(&i.([1]int)[0]) +} + +// Error: +// main/dylan/addressable_6c_err.gno:6:10: cannot take address of i.([(const (1 int))](const-type int))[(const (0 int))] From 89a041cb23feb2018ed7b5e0db43b0cde4303003 Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 28 Aug 2024 20:54:21 -0700 Subject: [PATCH 14/40] make sure to use base types when considering addressability --- gnovm/pkg/gnolang/preprocess.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e6693fc1cae..fae803382ac 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1347,7 +1347,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } if !n.IsAddressable && len(ft.Results) == 1 { - switch ft.Results[0].Type.(type) { + baseType := baseOf(ft.Results[0].Type) + switch baseType.(type) { case *PointerType, *SliceType: n.IsAddressable = true } @@ -1524,7 +1525,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Non-pointer arrays and strings produced by the slice expression are not addressable. // Slices and pointers are addressable. - switch t.(type) { + switch baseOf(t).(type) { case *ArrayType: if !n.X.isAddressable() { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) @@ -1717,7 +1718,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // only selectors of pointers or slices are addressable. Strings // are not included in this check, but will be marked as addressable // during the TRANS_LEAVE of SliceExpr if this is selecting a string. - if fieldType.Kind() == PointerKind || fieldType.Kind() == SliceKind { + switch baseOf(fieldType).(type) { + case *PointerType, *SliceType: n.IsAddressable = true } @@ -1823,6 +1825,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // packages may contain constant vars, // so check and evaluate if so. tt := pn.GetStaticTypeOfAt(store, n.Path) + + // Produce a constant expression for both typed and untyped constants. if isUntyped(tt) || pn.GetIsConstAt(store, n.Path) { cx := evalConst(store, last, n) return cx, TRANS_CONTINUE From 9956c2642129330798939755a6de9e29549a42ef Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 28 Aug 2024 20:57:21 -0700 Subject: [PATCH 15/40] move tests --- gnovm/tests/{dylan => files}/addressable_1.gno | 0 gnovm/tests/{dylan => files}/addressable_1a_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_1b_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_1c_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_1d_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_2.gno | 0 gnovm/tests/{dylan => files}/addressable_2a_err.gno | 0 gnovm/tests/{dylan => files}/addressable_3.gno | 0 gnovm/tests/{dylan => files}/addressable_3a_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_3b_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_4.gno | 0 gnovm/tests/{dylan => files}/addressable_4a_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_5.gno | 0 gnovm/tests/{dylan => files}/addressable_5a_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_5b_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_5c_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_5d_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_6.gno | 0 gnovm/tests/{dylan => files}/addressable_6a_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_6b_err.gno | 2 +- gnovm/tests/{dylan => files}/addressable_6c_err.gno | 2 +- 21 files changed, 14 insertions(+), 14 deletions(-) rename gnovm/tests/{dylan => files}/addressable_1.gno (100%) rename gnovm/tests/{dylan => files}/addressable_1a_err.gno (66%) rename gnovm/tests/{dylan => files}/addressable_1b_err.gno (63%) rename gnovm/tests/{dylan => files}/addressable_1c_err.gno (70%) rename gnovm/tests/{dylan => files}/addressable_1d_err.gno (66%) rename gnovm/tests/{dylan => files}/addressable_2.gno (100%) rename gnovm/tests/{dylan => files}/addressable_2a_err.gno (100%) rename gnovm/tests/{dylan => files}/addressable_3.gno (100%) rename gnovm/tests/{dylan => files}/addressable_3a_err.gno (77%) rename gnovm/tests/{dylan => files}/addressable_3b_err.gno (70%) rename gnovm/tests/{dylan => files}/addressable_4.gno (100%) rename gnovm/tests/{dylan => files}/addressable_4a_err.gno (65%) rename gnovm/tests/{dylan => files}/addressable_5.gno (100%) rename gnovm/tests/{dylan => files}/addressable_5a_err.gno (70%) rename gnovm/tests/{dylan => files}/addressable_5b_err.gno (70%) rename gnovm/tests/{dylan => files}/addressable_5c_err.gno (58%) rename gnovm/tests/{dylan => files}/addressable_5d_err.gno (56%) rename gnovm/tests/{dylan => files}/addressable_6.gno (100%) rename gnovm/tests/{dylan => files}/addressable_6a_err.gno (65%) rename gnovm/tests/{dylan => files}/addressable_6b_err.gno (71%) rename gnovm/tests/{dylan => files}/addressable_6c_err.gno (72%) diff --git a/gnovm/tests/dylan/addressable_1.gno b/gnovm/tests/files/addressable_1.gno similarity index 100% rename from gnovm/tests/dylan/addressable_1.gno rename to gnovm/tests/files/addressable_1.gno diff --git a/gnovm/tests/dylan/addressable_1a_err.gno b/gnovm/tests/files/addressable_1a_err.gno similarity index 66% rename from gnovm/tests/dylan/addressable_1a_err.gno rename to gnovm/tests/files/addressable_1a_err.gno index e6a9da10048..a8c8c3b3c77 100644 --- a/gnovm/tests/dylan/addressable_1a_err.gno +++ b/gnovm/tests/files/addressable_1a_err.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/dylan/addressable_1a_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))}[(const (0 int))] +// main/files/addressable_1a_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))}[(const (0 int))] diff --git a/gnovm/tests/dylan/addressable_1b_err.gno b/gnovm/tests/files/addressable_1b_err.gno similarity index 63% rename from gnovm/tests/dylan/addressable_1b_err.gno rename to gnovm/tests/files/addressable_1b_err.gno index 038d1dd3c26..9d8a7d12e4b 100644 --- a/gnovm/tests/dylan/addressable_1b_err.gno +++ b/gnovm/tests/files/addressable_1b_err.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/dylan/addressable_1b_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))} +// main/files/addressable_1b_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))} diff --git a/gnovm/tests/dylan/addressable_1c_err.gno b/gnovm/tests/files/addressable_1c_err.gno similarity index 70% rename from gnovm/tests/dylan/addressable_1c_err.gno rename to gnovm/tests/files/addressable_1c_err.gno index ada7146a9a1..420531187df 100644 --- a/gnovm/tests/dylan/addressable_1c_err.gno +++ b/gnovm/tests/files/addressable_1c_err.gno @@ -10,4 +10,4 @@ func getArr() [1]int { } // Error: -// main/dylan/addressable_1c_err.gno:4:6: cannot take address of getArr()[(const (0 int))] +// main/files/addressable_1c_err.gno:4:6: cannot take address of getArr()[(const (0 int))] diff --git a/gnovm/tests/dylan/addressable_1d_err.gno b/gnovm/tests/files/addressable_1d_err.gno similarity index 66% rename from gnovm/tests/dylan/addressable_1d_err.gno rename to gnovm/tests/files/addressable_1d_err.gno index 57dab7c35f3..56bb81c881f 100644 --- a/gnovm/tests/dylan/addressable_1d_err.gno +++ b/gnovm/tests/files/addressable_1d_err.gno @@ -9,4 +9,4 @@ func getArr() [1]int { } // Error: -// main/dylan/addressable_1d_err.gno:4:6: cannot take address of getArr() +// main/files/addressable_1d_err.gno:4:6: cannot take address of getArr() diff --git a/gnovm/tests/dylan/addressable_2.gno b/gnovm/tests/files/addressable_2.gno similarity index 100% rename from gnovm/tests/dylan/addressable_2.gno rename to gnovm/tests/files/addressable_2.gno diff --git a/gnovm/tests/dylan/addressable_2a_err.gno b/gnovm/tests/files/addressable_2a_err.gno similarity index 100% rename from gnovm/tests/dylan/addressable_2a_err.gno rename to gnovm/tests/files/addressable_2a_err.gno diff --git a/gnovm/tests/dylan/addressable_3.gno b/gnovm/tests/files/addressable_3.gno similarity index 100% rename from gnovm/tests/dylan/addressable_3.gno rename to gnovm/tests/files/addressable_3.gno diff --git a/gnovm/tests/dylan/addressable_3a_err.gno b/gnovm/tests/files/addressable_3a_err.gno similarity index 77% rename from gnovm/tests/dylan/addressable_3a_err.gno rename to gnovm/tests/files/addressable_3a_err.gno index 2154f7e7bea..dcf3883e79b 100644 --- a/gnovm/tests/dylan/addressable_3a_err.gno +++ b/gnovm/tests/files/addressable_3a_err.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/dylan/addressable_3a_err.gno:10:6: cannot take address of S{i: (const (4 int))}.i +// main/files/addressable_3a_err.gno:10:6: cannot take address of S{i: (const (4 int))}.i diff --git a/gnovm/tests/dylan/addressable_3b_err.gno b/gnovm/tests/files/addressable_3b_err.gno similarity index 70% rename from gnovm/tests/dylan/addressable_3b_err.gno rename to gnovm/tests/files/addressable_3b_err.gno index c31a4692555..db4e810155a 100644 --- a/gnovm/tests/dylan/addressable_3b_err.gno +++ b/gnovm/tests/files/addressable_3b_err.gno @@ -13,4 +13,4 @@ func getStruct() S { } // Error: -// main/dylan/addressable_3b_err.gno:8:6: cannot take address of getStruct().i +// main/files/addressable_3b_err.gno:8:6: cannot take address of getStruct().i diff --git a/gnovm/tests/dylan/addressable_4.gno b/gnovm/tests/files/addressable_4.gno similarity index 100% rename from gnovm/tests/dylan/addressable_4.gno rename to gnovm/tests/files/addressable_4.gno diff --git a/gnovm/tests/dylan/addressable_4a_err.gno b/gnovm/tests/files/addressable_4a_err.gno similarity index 65% rename from gnovm/tests/dylan/addressable_4a_err.gno rename to gnovm/tests/files/addressable_4a_err.gno index cceab42c600..17481976f3a 100644 --- a/gnovm/tests/dylan/addressable_4a_err.gno +++ b/gnovm/tests/files/addressable_4a_err.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/dylan/addressable_4a_err.gno:5:6: cannot take address of greeting[(const (2 int))] +// main/files/addressable_4a_err.gno:5:6: cannot take address of greeting[(const (2 int))] diff --git a/gnovm/tests/dylan/addressable_5.gno b/gnovm/tests/files/addressable_5.gno similarity index 100% rename from gnovm/tests/dylan/addressable_5.gno rename to gnovm/tests/files/addressable_5.gno diff --git a/gnovm/tests/dylan/addressable_5a_err.gno b/gnovm/tests/files/addressable_5a_err.gno similarity index 70% rename from gnovm/tests/dylan/addressable_5a_err.gno rename to gnovm/tests/files/addressable_5a_err.gno index c41f1ee086f..2755d47ef62 100644 --- a/gnovm/tests/dylan/addressable_5a_err.gno +++ b/gnovm/tests/files/addressable_5a_err.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/dylan/addressable_5a_err.gno:7:6: cannot take address of (const (255 bigint)) +// main/files/addressable_5a_err.gno:7:6: cannot take address of (const (255 bigint)) diff --git a/gnovm/tests/dylan/addressable_5b_err.gno b/gnovm/tests/files/addressable_5b_err.gno similarity index 70% rename from gnovm/tests/dylan/addressable_5b_err.gno rename to gnovm/tests/files/addressable_5b_err.gno index 12e0f121195..3c211bdbbc3 100644 --- a/gnovm/tests/dylan/addressable_5b_err.gno +++ b/gnovm/tests/files/addressable_5b_err.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/dylan/addressable_5b_err.gno:7:6: cannot take address of (const (0 std.BankerType)) +// main/files/addressable_5b_err.gno:7:6: cannot take address of (const (0 std.BankerType)) diff --git a/gnovm/tests/dylan/addressable_5c_err.gno b/gnovm/tests/files/addressable_5c_err.gno similarity index 58% rename from gnovm/tests/dylan/addressable_5c_err.gno rename to gnovm/tests/files/addressable_5c_err.gno index f4489657db9..92de1aeb30a 100644 --- a/gnovm/tests/dylan/addressable_5c_err.gno +++ b/gnovm/tests/files/addressable_5c_err.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/dylan/addressable_5c_err.gno:6:6: cannot take address of (const (1 bigint)) +// main/files/addressable_5c_err.gno:6:6: cannot take address of (const (1 bigint)) diff --git a/gnovm/tests/dylan/addressable_5d_err.gno b/gnovm/tests/files/addressable_5d_err.gno similarity index 56% rename from gnovm/tests/dylan/addressable_5d_err.gno rename to gnovm/tests/files/addressable_5d_err.gno index 9d2c4ba1b78..fe78ed36681 100644 --- a/gnovm/tests/dylan/addressable_5d_err.gno +++ b/gnovm/tests/files/addressable_5d_err.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/dylan/addressable_5d_err.gno:6:6: cannot take address of (const (1 int)) +// main/files/addressable_5d_err.gno:6:6: cannot take address of (const (1 int)) diff --git a/gnovm/tests/dylan/addressable_6.gno b/gnovm/tests/files/addressable_6.gno similarity index 100% rename from gnovm/tests/dylan/addressable_6.gno rename to gnovm/tests/files/addressable_6.gno diff --git a/gnovm/tests/dylan/addressable_6a_err.gno b/gnovm/tests/files/addressable_6a_err.gno similarity index 65% rename from gnovm/tests/dylan/addressable_6a_err.gno rename to gnovm/tests/files/addressable_6a_err.gno index 8d4f2866f78..f27432fa81b 100644 --- a/gnovm/tests/dylan/addressable_6a_err.gno +++ b/gnovm/tests/files/addressable_6a_err.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/dylan/addressable_6a_err.gno:6:10: cannot take address of i.((const-type int)) +// main/files/addressable_6a_err.gno:6:10: cannot take address of i.((const-type int)) diff --git a/gnovm/tests/dylan/addressable_6b_err.gno b/gnovm/tests/files/addressable_6b_err.gno similarity index 71% rename from gnovm/tests/dylan/addressable_6b_err.gno rename to gnovm/tests/files/addressable_6b_err.gno index 6c9b7d449d5..ea2e19aa3c4 100644 --- a/gnovm/tests/dylan/addressable_6b_err.gno +++ b/gnovm/tests/files/addressable_6b_err.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/dylan/addressable_6b_err.gno:10:10: cannot take address of i.(S).a +// main/files/addressable_6b_err.gno:10:10: cannot take address of i.(S).a diff --git a/gnovm/tests/dylan/addressable_6c_err.gno b/gnovm/tests/files/addressable_6c_err.gno similarity index 72% rename from gnovm/tests/dylan/addressable_6c_err.gno rename to gnovm/tests/files/addressable_6c_err.gno index 9837999eff1..03666922eee 100644 --- a/gnovm/tests/dylan/addressable_6c_err.gno +++ b/gnovm/tests/files/addressable_6c_err.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/dylan/addressable_6c_err.gno:6:10: cannot take address of i.([(const (1 int))](const-type int))[(const (0 int))] +// main/files/addressable_6c_err.gno:6:10: cannot take address of i.([(const (1 int))](const-type int))[(const (0 int))] From 69407141daef506c258b9087481ce195fb6678c2 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Aug 2024 15:46:29 -0700 Subject: [PATCH 16/40] fix index and selector addressability --- gnovm/pkg/gnolang/nodes.go | 17 +++++++++++------ gnovm/pkg/gnolang/preprocess.go | 19 +++++++------------ gnovm/tests/files/addressable_3c_err.gno | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 gnovm/tests/files/addressable_3c_err.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index bcc70aa85b4..414b6c6591b 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -420,14 +420,19 @@ func (x *CallExpr) isAddressable() bool { type IndexExpr struct { // X[Index] Attributes - X Expr // expression - Index Expr // index expression - HasOK bool // if true, is form: `value, ok := [] - NotAddressable bool + X Expr // expression + Index Expr // index expression + HasOK bool // if true, is form: `value, ok := [] + IsAddressable bool // true if X is an array pointer or slice + XIsString bool // true if X is a string (never addressable) } func (x *IndexExpr) isAddressable() bool { - return !x.NotAddressable && x.X.isAddressable() + if x.XIsString { + return false + } + + return x.IsAddressable || x.X.isAddressable() } type SelectorExpr struct { // X.Sel @@ -435,7 +440,7 @@ type SelectorExpr struct { // X.Sel X Expr // expression Path ValuePath // set by preprocessor. Sel Name // field selector - IsAddressable bool + IsAddressable bool // true if X is a pointer } func (x *SelectorExpr) isAddressable() bool { diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index fae803382ac..0a0bf1a0f87 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1493,15 +1493,19 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { dt = dt.Elem() n.X = &StarExpr{X: n.X} n.X.SetAttribute(ATTR_PREPROCESSED, true) + n.IsAddressable = true } switch dt.Kind() { case StringKind, ArrayKind, SliceKind: // Replace const index with int *ConstExpr, // or if not const, assert integer type.. checkOrConvertIntegerKind(store, last, n.Index) - if dt.Kind() == StringKind { + if dt.Kind() == SliceKind { // A string index is not addressable. - n.NotAddressable = true + n.IsAddressable = true + } else if dt.Kind() == StringKind { + // Special case; string indexes are never addressable. + n.XIsString = true } case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) @@ -1705,7 +1709,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Set selector path based on xt's type. switch cxt := xt.(type) { case *PointerType, *DeclaredType, *StructType, *InterfaceType: - tr, _, rcvr, fieldType, aerr := findEmbeddedFieldType(lastpn.PkgPath, cxt, n.Sel, nil) + tr, _, rcvr, _, aerr := findEmbeddedFieldType(lastpn.PkgPath, cxt, n.Sel, nil) if aerr { panic(fmt.Sprintf("cannot access %s.%s from %s", cxt.String(), n.Sel, lastpn.PkgPath)) @@ -1714,15 +1718,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Sel, cxt.String())) } - // In the case where n.X is a non-pointer declared type or struct, - // only selectors of pointers or slices are addressable. Strings - // are not included in this check, but will be marked as addressable - // during the TRANS_LEAVE of SliceExpr if this is selecting a string. - switch baseOf(fieldType).(type) { - case *PointerType, *SliceType: - n.IsAddressable = true - } - if len(tr) > 1 { // (the last vp, tr[len(tr)-1], is for n.Sel) if debug { diff --git a/gnovm/tests/files/addressable_3c_err.gno b/gnovm/tests/files/addressable_3c_err.gno new file mode 100644 index 00000000000..536b5f77475 --- /dev/null +++ b/gnovm/tests/files/addressable_3c_err.gno @@ -0,0 +1,17 @@ +package main + +type MyStruct struct { + Mp *int +} + +func makeT() MyStruct { + x := 10 + return MyStruct{Mp: &x} +} + +func main() { + _ = &makeT().Mp +} + +// Error: +// main/files/addressable_3c_err.gno:13:6: cannot take address of makeT().Mp From 3f4f1b6ee9c516cd7c3a79aa8e1ada304fd2639a Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Aug 2024 16:30:01 -0700 Subject: [PATCH 17/40] introduce concept of addressability not applicable --- gnovm/pkg/gnolang/addressability.go | 9 ++ gnovm/pkg/gnolang/nodes.go | 134 +++++++++++++---------- gnovm/pkg/gnolang/preprocess.go | 14 ++- gnovm/tests/files/addressable_7a_err.gno | 12 ++ gnovm/tests/files/addressable_7b_err.gno | 9 ++ gnovm/tests/files/addressable_7c_err.gno | 8 ++ 6 files changed, 123 insertions(+), 63 deletions(-) create mode 100644 gnovm/pkg/gnolang/addressability.go create mode 100644 gnovm/tests/files/addressable_7a_err.gno create mode 100644 gnovm/tests/files/addressable_7b_err.gno create mode 100644 gnovm/tests/files/addressable_7c_err.gno diff --git a/gnovm/pkg/gnolang/addressability.go b/gnovm/pkg/gnolang/addressability.go new file mode 100644 index 00000000000..0f436ae3c0f --- /dev/null +++ b/gnovm/pkg/gnolang/addressability.go @@ -0,0 +1,9 @@ +package gnolang + +type Addressability int + +const ( + AddressabilityNotApplicable Addressability = iota + AddressabilitySatisfied + AddressabilityUnsatisfied +) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 414b6c6591b..0456ec3190f 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -328,7 +328,7 @@ var ( type Expr interface { Node assertExpr() - isAddressable() bool + addressability() Addressability } type Exprs []Expr @@ -375,8 +375,8 @@ type NameExpr struct { Name } -func (x *NameExpr) isAddressable() bool { - return true +func (x *NameExpr) addressability() Addressability { + return AddressabilitySatisfied } type NameExprs []NameExpr @@ -390,8 +390,8 @@ type BasicLitExpr struct { Value string } -func (x *BasicLitExpr) isAddressable() bool { - return false +func (x *BasicLitExpr) addressability() Addressability { + return AddressabilityUnsatisfied } type BinaryExpr struct { // (Left Op Right) @@ -401,21 +401,21 @@ type BinaryExpr struct { // (Left Op Right) Right Expr // right operand } -func (x *BinaryExpr) isAddressable() bool { - return false +func (x *BinaryExpr) addressability() Addressability { + return AddressabilityUnsatisfied } type CallExpr struct { // Func(Args) Attributes - Func Expr // function expression - Args Exprs // function arguments, if any. - Varg bool // if true, final arg is variadic. - NumArgs int // len(Args) or len(Args[0].Results) - IsAddressable bool + Func Expr // function expression + Args Exprs // function arguments, if any. + Varg bool // if true, final arg is variadic. + NumArgs int // len(Args) or len(Args[0].Results) + Addressability Addressability } -func (x *CallExpr) isAddressable() bool { - return x.IsAddressable +func (x *CallExpr) addressability() Addressability { + return x.Addressability } type IndexExpr struct { // X[Index] @@ -427,12 +427,16 @@ type IndexExpr struct { // X[Index] XIsString bool // true if X is a string (never addressable) } -func (x *IndexExpr) isAddressable() bool { +func (x *IndexExpr) addressability() Addressability { if x.XIsString { - return false + return AddressabilityUnsatisfied } - return x.IsAddressable || x.X.isAddressable() + if x.IsAddressable || x.X.addressability() == AddressabilitySatisfied { + return AddressabilitySatisfied + } + + return AddressabilityUnsatisfied } type SelectorExpr struct { // X.Sel @@ -443,8 +447,12 @@ type SelectorExpr struct { // X.Sel IsAddressable bool // true if X is a pointer } -func (x *SelectorExpr) isAddressable() bool { - return x.IsAddressable || x.X.isAddressable() +func (x *SelectorExpr) addressability() Addressability { + if x.IsAddressable || x.X.addressability() == AddressabilitySatisfied { + return AddressabilitySatisfied + } + + return AddressabilityUnsatisfied } type SliceExpr struct { // X[Low:High:Max] @@ -456,8 +464,12 @@ type SliceExpr struct { // X[Low:High:Max] IsAddressable bool } -func (x *SliceExpr) isAddressable() bool { - return x.IsAddressable +func (x *SliceExpr) addressability() Addressability { + if x.IsAddressable { + return AddressabilitySatisfied + } + + return AddressabilityUnsatisfied } // A StarExpr node represents an expression of the form @@ -468,8 +480,8 @@ type StarExpr struct { // *X X Expr // operand } -func (x *StarExpr) isAddressable() bool { - return x.X.isAddressable() +func (x *StarExpr) addressability() Addressability { + return x.X.addressability() } type RefExpr struct { // &X @@ -477,8 +489,8 @@ type RefExpr struct { // &X X Expr // operand } -func (x *RefExpr) isAddressable() bool { - return x.X.isAddressable() +func (x *RefExpr) addressability() Addressability { + return x.X.addressability() } type TypeAssertExpr struct { // X.(Type) @@ -489,8 +501,12 @@ type TypeAssertExpr struct { // X.(Type) IsAddressable bool } -func (x *TypeAssertExpr) isAddressable() bool { - return x.IsAddressable +func (x *TypeAssertExpr) addressability() Addressability { + if x.IsAddressable { + return AddressabilitySatisfied + } + + return AddressabilityUnsatisfied } // A UnaryExpr node represents a unary expression. Unary @@ -503,8 +519,8 @@ type UnaryExpr struct { // (Op X) Op Word // operator } -func (x *UnaryExpr) isAddressable() bool { - return x.X.isAddressable() +func (x *UnaryExpr) addressability() Addressability { + return x.X.addressability() } // MyType{:} struct, array, slice, and map @@ -516,8 +532,12 @@ type CompositeLitExpr struct { IsAddressable bool } -func (x *CompositeLitExpr) isAddressable() bool { - return x.IsAddressable +func (x *CompositeLitExpr) addressability() Addressability { + if x.IsAddressable { + return AddressabilitySatisfied + } + + return AddressabilityUnsatisfied } // Returns true if any elements are keyed. @@ -550,8 +570,8 @@ type KeyValueExpr struct { Value Expr // never nil } -func (x *KeyValueExpr) isAddressable() bool { - return false +func (x *KeyValueExpr) addressability() Addressability { + return AddressabilityNotApplicable } type KeyValueExprs []KeyValueExpr @@ -566,8 +586,8 @@ type FuncLitExpr struct { Body // function body } -func (x *FuncLitExpr) isAddressable() bool { - return false +func (x *FuncLitExpr) addressability() Addressability { + return AddressabilityUnsatisfied } // The preprocessor replaces const expressions @@ -578,8 +598,8 @@ type ConstExpr struct { TypedValue } -func (x *ConstExpr) isAddressable() bool { - return false +func (x *ConstExpr) addressability() Addressability { + return AddressabilityUnsatisfied } // ---------------------------------------- @@ -646,8 +666,8 @@ type FieldTypeExpr struct { Tag Expr } -func (x *FieldTypeExpr) isAddressable() bool { - return false +func (x *FieldTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type FieldTypeExprs []FieldTypeExpr @@ -674,8 +694,8 @@ type ArrayTypeExpr struct { Elt Expr // element type } -func (x *ArrayTypeExpr) isAddressable() bool { - return false +func (x *ArrayTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type SliceTypeExpr struct { @@ -684,8 +704,8 @@ type SliceTypeExpr struct { Vrd bool // variadic arg expression } -func (x *SliceTypeExpr) isAddressable() bool { - return false +func (x *SliceTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type InterfaceTypeExpr struct { @@ -694,8 +714,8 @@ type InterfaceTypeExpr struct { Generic Name // for uverse generics } -func (x *InterfaceTypeExpr) isAddressable() bool { - return false +func (x *InterfaceTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type ChanDir int @@ -715,8 +735,8 @@ type ChanTypeExpr struct { Value Expr // value type } -func (x *ChanTypeExpr) isAddressable() bool { - return false +func (x *ChanTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type FuncTypeExpr struct { @@ -725,8 +745,8 @@ type FuncTypeExpr struct { Results FieldTypeExprs // (outgoing) results, if any. } -func (x *FuncTypeExpr) isAddressable() bool { - return false +func (x *FuncTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type MapTypeExpr struct { @@ -735,8 +755,8 @@ type MapTypeExpr struct { Value Expr // value type } -func (x *MapTypeExpr) isAddressable() bool { - return false +func (x *MapTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } type StructTypeExpr struct { @@ -744,8 +764,8 @@ type StructTypeExpr struct { Fields FieldTypeExprs // list of field declarations } -func (x *StructTypeExpr) isAddressable() bool { - return false +func (x *StructTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } // Like ConstExpr but for types. @@ -755,8 +775,8 @@ type constTypeExpr struct { Type Type } -func (x *constTypeExpr) isAddressable() bool { - return false +func (x *constTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } // Only used for native func arguments @@ -765,8 +785,8 @@ type MaybeNativeTypeExpr struct { Type Expr } -func (x *MaybeNativeTypeExpr) isAddressable() bool { - return false +func (x *MaybeNativeTypeExpr) addressability() Addressability { + return AddressabilityNotApplicable } // ---------------------------------------- diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 0a0bf1a0f87..f36fad0c5ec 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1293,7 +1293,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { fv := cx.GetFunc() if fv.PkgPath == uversePkgPath && fv.Name == "append" { // append returns a slice and slices are always addressable. - n.IsAddressable = true + n.Addressability = AddressabilitySatisfied if n.Varg && len(n.Args) == 2 { // If the second argument is a string, // convert to byteslice. @@ -1342,15 +1342,17 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } else if fv.PkgPath == uversePkgPath && fv.Name == "new" { // new returns a pointer and pointers are always addressable. - n.IsAddressable = true + n.Addressability = AddressabilitySatisfied } } - if !n.IsAddressable && len(ft.Results) == 1 { + if n.Addressability != AddressabilitySatisfied && len(ft.Results) == 1 { baseType := baseOf(ft.Results[0].Type) switch baseType.(type) { case *PointerType, *SliceType: - n.IsAddressable = true + n.Addressability = AddressabilitySatisfied + default: + n.Addressability = AddressabilityUnsatisfied } } @@ -1531,7 +1533,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Slices and pointers are addressable. switch baseOf(t).(type) { case *ArrayType: - if !n.X.isAddressable() { + if n.X.addressability() == AddressabilityUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } @@ -2421,7 +2423,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Type = constType(n.Type, dst) case *RefExpr: - if !n.X.isAddressable() { + if n.X.addressability() == AddressabilityUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } } diff --git a/gnovm/tests/files/addressable_7a_err.gno b/gnovm/tests/files/addressable_7a_err.gno new file mode 100644 index 00000000000..f3648b6b990 --- /dev/null +++ b/gnovm/tests/files/addressable_7a_err.gno @@ -0,0 +1,12 @@ +package main + +func foo() ([]int, []string) { + return []int{1, 2, 3}, []string{"a", "b", "c"} +} + +func main() { + _ = &foo() +} + +// Error: +// main/files/addressable_7a_err.gno:8:2: getTypeOf() only supports *CallExpr with 1 result, got ([]int,[]string) diff --git a/gnovm/tests/files/addressable_7b_err.gno b/gnovm/tests/files/addressable_7b_err.gno new file mode 100644 index 00000000000..1e7f3cb6e17 --- /dev/null +++ b/gnovm/tests/files/addressable_7b_err.gno @@ -0,0 +1,9 @@ +package main + +type MyInt int + +func main() { + _ = &MyInt +} + +// Error: diff --git a/gnovm/tests/files/addressable_7c_err.gno b/gnovm/tests/files/addressable_7c_err.gno new file mode 100644 index 00000000000..133d6f3af8b --- /dev/null +++ b/gnovm/tests/files/addressable_7c_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _, _ = &int(9) +} + +// Error: +// main/files/addressable_7c_err.gno:4:9: cannot take address of (const (9 int)) From c16e5e9df065705616985d6f835a0b41b83b81ed Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Aug 2024 16:51:14 -0700 Subject: [PATCH 18/40] fixed test --- .../{addressable_5a_err.gno => addressable_5a_err_stdlibs.gno} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename gnovm/tests/files/{addressable_5a_err.gno => addressable_5a_err_stdlibs.gno} (53%) diff --git a/gnovm/tests/files/addressable_5a_err.gno b/gnovm/tests/files/addressable_5a_err_stdlibs.gno similarity index 53% rename from gnovm/tests/files/addressable_5a_err.gno rename to gnovm/tests/files/addressable_5a_err_stdlibs.gno index 2755d47ef62..bc6318f511d 100644 --- a/gnovm/tests/files/addressable_5a_err.gno +++ b/gnovm/tests/files/addressable_5a_err_stdlibs.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/addressable_5a_err.gno:7:6: cannot take address of (const (255 bigint)) +// main/files/addressable_5a_err_stdlibs.gno:7:6: cannot take address of (const (255 bigint)) From ac79f00acfec84818d6d3c4f4998de0c91f5d745 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Aug 2024 16:58:56 -0700 Subject: [PATCH 19/40] fixed test --- .../{addressable_5b_err.gno => addressable_5b_err_stdlibs.gno} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename gnovm/tests/files/{addressable_5b_err.gno => addressable_5b_err_stdlibs.gno} (55%) diff --git a/gnovm/tests/files/addressable_5b_err.gno b/gnovm/tests/files/addressable_5b_err_stdlibs.gno similarity index 55% rename from gnovm/tests/files/addressable_5b_err.gno rename to gnovm/tests/files/addressable_5b_err_stdlibs.gno index 3c211bdbbc3..e2028a22c9e 100644 --- a/gnovm/tests/files/addressable_5b_err.gno +++ b/gnovm/tests/files/addressable_5b_err_stdlibs.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/addressable_5b_err.gno:7:6: cannot take address of (const (0 std.BankerType)) +// main/files/addressable_5b_err_stdlibs.gno:7:6: cannot take address of (const (0 std.BankerType)) From df764cd5e40379ff2ffa97688012153b1b82ed24 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Aug 2024 18:24:56 -0700 Subject: [PATCH 20/40] fix test --- gnovm/tests/files/addressable_3.gno | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gnovm/tests/files/addressable_3.gno b/gnovm/tests/files/addressable_3.gno index 1a955b97fe7..34e9a2e1edc 100644 --- a/gnovm/tests/files/addressable_3.gno +++ b/gnovm/tests/files/addressable_3.gno @@ -87,17 +87,17 @@ func getStructPtr() *S { // &(5 int) // &(0 int) // &(6 int) -// +// // &(4 int) -// +// // 9 // &(0 int) // &(6 int) -// +// // 9 // &(0 int) // &(6 int) -// +// // &(4 int) // 9 // &(5 int) From af12ca140043803d2c4f028d31968cabf67814d2 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Aug 2024 18:31:57 -0700 Subject: [PATCH 21/40] func lit tests --- gnovm/tests/files/addressable_8.gno | 9 +++++++++ gnovm/tests/files/addressable_8a_err.gno | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 gnovm/tests/files/addressable_8.gno create mode 100644 gnovm/tests/files/addressable_8a_err.gno diff --git a/gnovm/tests/files/addressable_8.gno b/gnovm/tests/files/addressable_8.gno new file mode 100644 index 00000000000..b28c4292661 --- /dev/null +++ b/gnovm/tests/files/addressable_8.gno @@ -0,0 +1,9 @@ +package main + +func main() { + f := func() { println("Hello, World!") } + println(&f) +} + +// Output: +// &( func()()) diff --git a/gnovm/tests/files/addressable_8a_err.gno b/gnovm/tests/files/addressable_8a_err.gno new file mode 100644 index 00000000000..6e3395da773 --- /dev/null +++ b/gnovm/tests/files/addressable_8a_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = &func() { println("Hello, World!") } +} + +// Error: +// main/files/addressable_8a_err.gno:4:6: cannot take address of func func(){ (const (println func(xs ...interface{})()))((const ("Hello, World!" string))) } From e498f1ccab77a922abdb956ac2b111d894647878 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 30 Aug 2024 11:14:49 -0700 Subject: [PATCH 22/40] index, map, and call expr fixes and tests --- gnovm/pkg/gnolang/nodes.go | 20 ++++++-------- gnovm/pkg/gnolang/preprocess.go | 25 +++++++++++++----- gnovm/tests/files/addressable_10.gno | 12 +++++++++ gnovm/tests/files/addressable_10a_err.gno | 14 ++++++++++ gnovm/tests/files/addressable_10b_err.gno | 12 +++++++++ gnovm/tests/files/addressable_9.gno | 32 +++++++++++++++++++++++ gnovm/tests/files/addressable_9a_err.gno | 10 +++++++ gnovm/tests/files/addressable_9b_err.gno | 15 +++++++++++ 8 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 gnovm/tests/files/addressable_10.gno create mode 100644 gnovm/tests/files/addressable_10a_err.gno create mode 100644 gnovm/tests/files/addressable_10b_err.gno create mode 100644 gnovm/tests/files/addressable_9.gno create mode 100644 gnovm/tests/files/addressable_9a_err.gno create mode 100644 gnovm/tests/files/addressable_9b_err.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 0456ec3190f..ceb20289d4c 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -420,23 +420,19 @@ func (x *CallExpr) addressability() Addressability { type IndexExpr struct { // X[Index] Attributes - X Expr // expression - Index Expr // index expression - HasOK bool // if true, is form: `value, ok := [] - IsAddressable bool // true if X is an array pointer or slice - XIsString bool // true if X is a string (never addressable) + X Expr // expression + Index Expr // index expression + HasOK bool // if true, is form: `value, ok := [] + Addressability Addressability } func (x *IndexExpr) addressability() Addressability { - if x.XIsString { - return AddressabilityUnsatisfied - } - - if x.IsAddressable || x.X.addressability() == AddressabilitySatisfied { - return AddressabilitySatisfied + // In this case NotApplicable means that it wasn't set, the default value. + if x.Addressability == AddressabilityNotApplicable { + return x.X.addressability() } - return AddressabilityUnsatisfied + return x.Addressability } type SelectorExpr struct { // X.Sel diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index f36fad0c5ec..571677d4409 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1342,14 +1342,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } else if fv.PkgPath == uversePkgPath && fv.Name == "new" { // new returns a pointer and pointers are always addressable. - n.Addressability = AddressabilitySatisfied + n.Addressability = AddressabilityUnsatisfied } } if n.Addressability != AddressabilitySatisfied && len(ft.Results) == 1 { - baseType := baseOf(ft.Results[0].Type) - switch baseType.(type) { - case *PointerType, *SliceType: + switch ft.Results[0].Type.Kind() { + case SliceKind: n.Addressability = AddressabilitySatisfied default: n.Addressability = AddressabilityUnsatisfied @@ -1495,7 +1494,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { dt = dt.Elem() n.X = &StarExpr{X: n.X} n.X.SetAttribute(ATTR_PREPROCESSED, true) - n.IsAddressable = true + n.Addressability = AddressabilitySatisfied } switch dt.Kind() { case StringKind, ArrayKind, SliceKind: @@ -1504,14 +1503,26 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { checkOrConvertIntegerKind(store, last, n.Index) if dt.Kind() == SliceKind { // A string index is not addressable. - n.IsAddressable = true + n.Addressability = AddressabilitySatisfied } else if dt.Kind() == StringKind { // Special case; string indexes are never addressable. - n.XIsString = true + n.Addressability = AddressabilityUnsatisfied } case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) checkOrConvertType(store, last, &n.Index, mt.Key, false) + switch mt.Value.Kind() { + case SliceKind: + n.Addressability = AddressabilitySatisfied + case StringKind: + n.Addressability = AddressabilityUnsatisfied + case PointerKind: + if baseOf(mt.Value).(*PointerType).Elt.Kind() == ArrayKind { + n.Addressability = AddressabilitySatisfied + } + default: + n.Addressability = AddressabilityUnsatisfied + } default: panic(fmt.Sprintf( "unexpected index base kind for type %s", diff --git a/gnovm/tests/files/addressable_10.gno b/gnovm/tests/files/addressable_10.gno new file mode 100644 index 00000000000..9052c172973 --- /dev/null +++ b/gnovm/tests/files/addressable_10.gno @@ -0,0 +1,12 @@ +package main + +type S struct { + i int +} + +func main() { + println(&new(S).i) +} + +// Output: +// &(0 int) diff --git a/gnovm/tests/files/addressable_10a_err.gno b/gnovm/tests/files/addressable_10a_err.gno new file mode 100644 index 00000000000..6781c6c67e5 --- /dev/null +++ b/gnovm/tests/files/addressable_10a_err.gno @@ -0,0 +1,14 @@ +package main + +func main() { + println(&getPtr()) +} + +type S struct{} + +func getPtr() *S { + return &S{} +} + +// Error: +// main/files/addressable_10a_err.gno:4:10: cannot take address of getPtr() diff --git a/gnovm/tests/files/addressable_10b_err.gno b/gnovm/tests/files/addressable_10b_err.gno new file mode 100644 index 00000000000..30048dfa8f6 --- /dev/null +++ b/gnovm/tests/files/addressable_10b_err.gno @@ -0,0 +1,12 @@ +package main + +type S struct { + i int +} + +func main() { + println(&new(S)) +} + +// Error: +// main/files/addressable_10b_err.gno:8:10: cannot take address of (const (new func(t type{})( *main.S)))(S) diff --git a/gnovm/tests/files/addressable_9.gno b/gnovm/tests/files/addressable_9.gno new file mode 100644 index 00000000000..3b0b832f781 --- /dev/null +++ b/gnovm/tests/files/addressable_9.gno @@ -0,0 +1,32 @@ +package main + +type S struct { + i int + t *T +} + +type T struct { + i int +} + +func main() { + m := map[int]*S{} + s := &S{i: 4} + m[5] = s + println(&m[5].i) + + mm := map[int]S{} + ss := S{t: new(T)} + mm[8] = ss + println(&mm[8].t.i) + + mmm := map[int]map[int]*S{} + mmm[3] = map[int]*S{} + mmm[3][3] = &S{i: 7} + println(&mmm[3][3].i) +} + +// Output: +// &(4 int) +// &(0 int) +// &(7 int) diff --git a/gnovm/tests/files/addressable_9a_err.gno b/gnovm/tests/files/addressable_9a_err.gno new file mode 100644 index 00000000000..038990e1c01 --- /dev/null +++ b/gnovm/tests/files/addressable_9a_err.gno @@ -0,0 +1,10 @@ +package main + +func main() { + m := map[int]int{} + m[4] = 5 + println(&m[4]) +} + +// Error: +// main/files/addressable_9a_err.gno:6:10: cannot take address of m[(const (4 int))] diff --git a/gnovm/tests/files/addressable_9b_err.gno b/gnovm/tests/files/addressable_9b_err.gno new file mode 100644 index 00000000000..92db1fbeefc --- /dev/null +++ b/gnovm/tests/files/addressable_9b_err.gno @@ -0,0 +1,15 @@ +package main + +type S struct { + i int +} + +func main() { + mmm := map[int]map[int]S{} + mmm[3] = map[int]S{} + mmm[3][3] = S{i: 7} + println(&mmm[3][3].i) +} + +// Error: +// main/files/addressable_9b_err.gno:11:10: cannot take address of mmm[(const (3 int))][(const (3 int))].i From bee868ca4a7a6eb23ff9c7f10302a7a0b3fd9575 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 30 Aug 2024 15:14:26 -0700 Subject: [PATCH 23/40] addressability consistentcy fixes --- gnovm/pkg/gnolang/nodes.go | 13 +++----- gnovm/pkg/gnolang/preprocess.go | 41 ++---------------------- gnovm/tests/files/addressable_2a_err.gno | 2 +- gnovm/tests/files/addressable_2b_err.gno | 12 +++++++ gnovm/tests/files/addressable_6d_err.gno | 12 +++++++ 5 files changed, 32 insertions(+), 48 deletions(-) create mode 100644 gnovm/tests/files/addressable_2b_err.gno create mode 100644 gnovm/tests/files/addressable_6d_err.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index ceb20289d4c..1bbfcabb12b 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -453,18 +453,13 @@ func (x *SelectorExpr) addressability() Addressability { type SliceExpr struct { // X[Low:High:Max] Attributes - X Expr // expression - Low Expr // begin of slice range; or nil - High Expr // end of slice range; or nil - Max Expr // maximum capacity of slice; or nil; added in Go 1.2 - IsAddressable bool + X Expr // expression + Low Expr // begin of slice range; or nil + High Expr // end of slice range; or nil + Max Expr // maximum capacity of slice; or nil; added in Go 1.2 } func (x *SliceExpr) addressability() Addressability { - if x.IsAddressable { - return AddressabilitySatisfied - } - return AddressabilityUnsatisfied } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 571677d4409..4c064bc5d19 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1347,12 +1347,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } if n.Addressability != AddressabilitySatisfied && len(ft.Results) == 1 { - switch ft.Results[0].Type.Kind() { - case SliceKind: - n.Addressability = AddressabilitySatisfied - default: - n.Addressability = AddressabilityUnsatisfied - } + n.Addressability = AddressabilityUnsatisfied } // Continue with general case. @@ -1511,18 +1506,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) checkOrConvertType(store, last, &n.Index, mt.Key, false) - switch mt.Value.Kind() { - case SliceKind: - n.Addressability = AddressabilitySatisfied - case StringKind: - n.Addressability = AddressabilityUnsatisfied - case PointerKind: - if baseOf(mt.Value).(*PointerType).Elt.Kind() == ArrayKind { - n.Addressability = AddressabilitySatisfied - } - default: - n.Addressability = AddressabilityUnsatisfied - } + n.Addressability = AddressabilityUnsatisfied default: panic(fmt.Sprintf( "unexpected index base kind for type %s", @@ -1538,19 +1522,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { checkOrConvertIntegerKind(store, last, n.Max) t := evalStaticTypeOf(store, last, n.X) - n.IsAddressable = true - - // Non-pointer arrays and strings produced by the slice expression are not addressable. - // Slices and pointers are addressable. - switch baseOf(t).(type) { - case *ArrayType: + if t.Kind() == ArrayKind { if n.X.addressability() == AddressabilityUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } - - n.IsAddressable = false - case *PrimitiveType: - n.IsAddressable = false } // if n.X is untyped, convert to corresponding type @@ -1591,16 +1566,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ) } - targetType := baseOf(evalStaticType(store, last, n.Type)) - switch targetType.(type) { - case *PointerType, *SliceType: - // If the type assertion is to a pointer of slice, the result is addressable. - // Type assertions for other types result in a copy of the value or an - // interface, both non-addressable. Slices are addressable because the copy - // of the slice still references the underlying array. - n.IsAddressable = true - } - // TRANS_LEAVE ----------------------- case *UnaryExpr: xt := evalStaticTypeOf(store, last, n.X) diff --git a/gnovm/tests/files/addressable_2a_err.gno b/gnovm/tests/files/addressable_2a_err.gno index 42b04868f13..363fa3d2817 100644 --- a/gnovm/tests/files/addressable_2a_err.gno +++ b/gnovm/tests/files/addressable_2a_err.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// illegal assignment X expression type *gnolang.SliceExpr +// main/files/addressable_2a_err.gno:4:6: cannot take address of [](const-type int){(const (1 int))}[:] diff --git a/gnovm/tests/files/addressable_2b_err.gno b/gnovm/tests/files/addressable_2b_err.gno new file mode 100644 index 00000000000..eef7fd37ca5 --- /dev/null +++ b/gnovm/tests/files/addressable_2b_err.gno @@ -0,0 +1,12 @@ +package main + +func main() { + _ = &getSlice() +} + +func getSlice() []int { + return []int{1} +} + +// Error: +// main/files/addressable_2b_err.gno:4:6: cannot take address of getSlice() diff --git a/gnovm/tests/files/addressable_6d_err.gno b/gnovm/tests/files/addressable_6d_err.gno new file mode 100644 index 00000000000..b6058b7c024 --- /dev/null +++ b/gnovm/tests/files/addressable_6d_err.gno @@ -0,0 +1,12 @@ +package main + +func main() { + _ = &getSlice().([]int) +} + +func getSlice() interface{} { + return []int{1} +} + +// Error: +// main/files/addressable_6d_err.gno:4:6: cannot take address of getSlice().([](const-type int)) From 96984614cb6306cea058a13e507778996f772ea3 Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 4 Sep 2024 10:51:12 -0700 Subject: [PATCH 24/40] consolidate simplest block path traversal --- gnovm/pkg/gnolang/nodes.go | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 1bbfcabb12b..e0b4895f56c 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1795,10 +1795,19 @@ func (sb *StaticBlock) GetIsConst(store Store, n Name) bool { } } -func (sb *StaticBlock) GetIsConstAt(store Store, path ValuePath) bool { +func (sb *StaticBlock) getAt(store Store, path ValuePath) *StaticBlock { + if debug { + if path.Type != VPBlock { + panic("should not happen") + } + if path.Depth == 0 { + panic("should not happen") + } + } + for { if path.Depth == 1 { - return sb.getLocalIsConst(path.Name) + return sb } else { sb = sb.GetParentNode(store).GetStaticBlock() path.Depth -= 1 @@ -1806,6 +1815,10 @@ func (sb *StaticBlock) GetIsConstAt(store Store, path ValuePath) bool { } } +func (sb *StaticBlock) GetIsConstAt(store Store, path ValuePath) bool { + return sb.getAt(store, path).getLocalIsConst(path.Name) +} + // Returns true iff n is a local const defined name. func (sb *StaticBlock) getLocalIsConst(n Name) bool { for _, name := range sb.Consts { @@ -1841,22 +1854,7 @@ func (sb *StaticBlock) GetStaticTypeOf(store Store, n Name) Type { // Implements BlockNode. func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { - if debug { - if path.Type != VPBlock { - panic("should not happen") - } - if path.Depth == 0 { - panic("should not happen") - } - } - for { - if path.Depth == 1 { - return sb.Types[path.Index] - } else { - sb = sb.GetParentNode(store).GetStaticBlock() - path.Depth -= 1 - } - } + return sb.getAt(store, path).Types[path.Index] } // Implements BlockNode. From b284b56d627d8db9ec0f563b7c874d01871de9aa Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 6 Sep 2024 16:40:57 -0700 Subject: [PATCH 25/40] fix comment --- gnovm/pkg/gnolang/preprocess.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 4c064bc5d19..8dfcd70c9cc 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1341,7 +1341,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } else if fv.PkgPath == uversePkgPath && fv.Name == "new" { - // new returns a pointer and pointers are always addressable. + // The pointer value returned is not addressable, but maybe some selector + // will make it addressable. For now mark it as not addressable. n.Addressability = AddressabilityUnsatisfied } } From dfe024f4fd33bf1f45e29ed4b1161b40755ad8e3 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 12 Sep 2024 11:23:26 -0400 Subject: [PATCH 26/40] Removed test and renamed other --- gnovm/tests/files/addressable_7b_err.gno | 5 ++--- gnovm/tests/files/addressable_7c_err.gno | 8 -------- 2 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 gnovm/tests/files/addressable_7c_err.gno diff --git a/gnovm/tests/files/addressable_7b_err.gno b/gnovm/tests/files/addressable_7b_err.gno index 1e7f3cb6e17..133d6f3af8b 100644 --- a/gnovm/tests/files/addressable_7b_err.gno +++ b/gnovm/tests/files/addressable_7b_err.gno @@ -1,9 +1,8 @@ package main -type MyInt int - func main() { - _ = &MyInt + _, _ = &int(9) } // Error: +// main/files/addressable_7c_err.gno:4:9: cannot take address of (const (9 int)) diff --git a/gnovm/tests/files/addressable_7c_err.gno b/gnovm/tests/files/addressable_7c_err.gno deleted file mode 100644 index 133d6f3af8b..00000000000 --- a/gnovm/tests/files/addressable_7c_err.gno +++ /dev/null @@ -1,8 +0,0 @@ -package main - -func main() { - _, _ = &int(9) -} - -// Error: -// main/files/addressable_7c_err.gno:4:9: cannot take address of (const (9 int)) From 0e808014471e50b0e7e2f87bc7950f183caf0ab2 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 19 Sep 2024 12:24:48 -1000 Subject: [PATCH 27/40] move and rename addressability constants --- gnovm/pkg/gnolang/addressability.go | 9 --- gnovm/pkg/gnolang/nodes.go | 114 +++++++++++++++------------- gnovm/pkg/gnolang/preprocess.go | 20 ++--- 3 files changed, 71 insertions(+), 72 deletions(-) delete mode 100644 gnovm/pkg/gnolang/addressability.go diff --git a/gnovm/pkg/gnolang/addressability.go b/gnovm/pkg/gnolang/addressability.go deleted file mode 100644 index 0f436ae3c0f..00000000000 --- a/gnovm/pkg/gnolang/addressability.go +++ /dev/null @@ -1,9 +0,0 @@ -package gnolang - -type Addressability int - -const ( - AddressabilityNotApplicable Addressability = iota - AddressabilitySatisfied - AddressabilityUnsatisfied -) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index e0b4895f56c..2779a4d4e8a 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -328,7 +328,7 @@ var ( type Expr interface { Node assertExpr() - addressability() Addressability + addressability() addressabilityStatus } type Exprs []Expr @@ -375,8 +375,8 @@ type NameExpr struct { Name } -func (x *NameExpr) addressability() Addressability { - return AddressabilitySatisfied +func (x *NameExpr) addressability() addressabilityStatus { + return addressabilityStatusSatisfied } type NameExprs []NameExpr @@ -390,8 +390,8 @@ type BasicLitExpr struct { Value string } -func (x *BasicLitExpr) addressability() Addressability { - return AddressabilityUnsatisfied +func (x *BasicLitExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied } type BinaryExpr struct { // (Left Op Right) @@ -401,8 +401,8 @@ type BinaryExpr struct { // (Left Op Right) Right Expr // right operand } -func (x *BinaryExpr) addressability() Addressability { - return AddressabilityUnsatisfied +func (x *BinaryExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied } type CallExpr struct { // Func(Args) @@ -411,10 +411,10 @@ type CallExpr struct { // Func(Args) Args Exprs // function arguments, if any. Varg bool // if true, final arg is variadic. NumArgs int // len(Args) or len(Args[0].Results) - Addressability Addressability + Addressability addressabilityStatus } -func (x *CallExpr) addressability() Addressability { +func (x *CallExpr) addressability() addressabilityStatus { return x.Addressability } @@ -423,12 +423,12 @@ type IndexExpr struct { // X[Index] X Expr // expression Index Expr // index expression HasOK bool // if true, is form: `value, ok := [] - Addressability Addressability + Addressability addressabilityStatus } -func (x *IndexExpr) addressability() Addressability { +func (x *IndexExpr) addressability() addressabilityStatus { // In this case NotApplicable means that it wasn't set, the default value. - if x.Addressability == AddressabilityNotApplicable { + if x.Addressability == addressabilityStatusNotApplicable { return x.X.addressability() } @@ -443,12 +443,12 @@ type SelectorExpr struct { // X.Sel IsAddressable bool // true if X is a pointer } -func (x *SelectorExpr) addressability() Addressability { - if x.IsAddressable || x.X.addressability() == AddressabilitySatisfied { - return AddressabilitySatisfied +func (x *SelectorExpr) addressability() addressabilityStatus { + if x.IsAddressable || x.X.addressability() == addressabilityStatusSatisfied { + return addressabilityStatusSatisfied } - return AddressabilityUnsatisfied + return addressabilityStatusUnsatisfied } type SliceExpr struct { // X[Low:High:Max] @@ -459,8 +459,8 @@ type SliceExpr struct { // X[Low:High:Max] Max Expr // maximum capacity of slice; or nil; added in Go 1.2 } -func (x *SliceExpr) addressability() Addressability { - return AddressabilityUnsatisfied +func (x *SliceExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied } // A StarExpr node represents an expression of the form @@ -471,7 +471,7 @@ type StarExpr struct { // *X X Expr // operand } -func (x *StarExpr) addressability() Addressability { +func (x *StarExpr) addressability() addressabilityStatus { return x.X.addressability() } @@ -480,7 +480,7 @@ type RefExpr struct { // &X X Expr // operand } -func (x *RefExpr) addressability() Addressability { +func (x *RefExpr) addressability() addressabilityStatus { return x.X.addressability() } @@ -492,12 +492,12 @@ type TypeAssertExpr struct { // X.(Type) IsAddressable bool } -func (x *TypeAssertExpr) addressability() Addressability { +func (x *TypeAssertExpr) addressability() addressabilityStatus { if x.IsAddressable { - return AddressabilitySatisfied + return addressabilityStatusSatisfied } - return AddressabilityUnsatisfied + return addressabilityStatusUnsatisfied } // A UnaryExpr node represents a unary expression. Unary @@ -510,7 +510,7 @@ type UnaryExpr struct { // (Op X) Op Word // operator } -func (x *UnaryExpr) addressability() Addressability { +func (x *UnaryExpr) addressability() addressabilityStatus { return x.X.addressability() } @@ -523,12 +523,12 @@ type CompositeLitExpr struct { IsAddressable bool } -func (x *CompositeLitExpr) addressability() Addressability { +func (x *CompositeLitExpr) addressability() addressabilityStatus { if x.IsAddressable { - return AddressabilitySatisfied + return addressabilityStatusSatisfied } - return AddressabilityUnsatisfied + return addressabilityStatusUnsatisfied } // Returns true if any elements are keyed. @@ -561,8 +561,8 @@ type KeyValueExpr struct { Value Expr // never nil } -func (x *KeyValueExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *KeyValueExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type KeyValueExprs []KeyValueExpr @@ -577,8 +577,8 @@ type FuncLitExpr struct { Body // function body } -func (x *FuncLitExpr) addressability() Addressability { - return AddressabilityUnsatisfied +func (x *FuncLitExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied } // The preprocessor replaces const expressions @@ -589,8 +589,8 @@ type ConstExpr struct { TypedValue } -func (x *ConstExpr) addressability() Addressability { - return AddressabilityUnsatisfied +func (x *ConstExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied } // ---------------------------------------- @@ -657,8 +657,8 @@ type FieldTypeExpr struct { Tag Expr } -func (x *FieldTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *FieldTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type FieldTypeExprs []FieldTypeExpr @@ -685,8 +685,8 @@ type ArrayTypeExpr struct { Elt Expr // element type } -func (x *ArrayTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *ArrayTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type SliceTypeExpr struct { @@ -695,8 +695,8 @@ type SliceTypeExpr struct { Vrd bool // variadic arg expression } -func (x *SliceTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *SliceTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type InterfaceTypeExpr struct { @@ -705,8 +705,8 @@ type InterfaceTypeExpr struct { Generic Name // for uverse generics } -func (x *InterfaceTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *InterfaceTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type ChanDir int @@ -726,8 +726,8 @@ type ChanTypeExpr struct { Value Expr // value type } -func (x *ChanTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *ChanTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type FuncTypeExpr struct { @@ -736,8 +736,8 @@ type FuncTypeExpr struct { Results FieldTypeExprs // (outgoing) results, if any. } -func (x *FuncTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *FuncTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type MapTypeExpr struct { @@ -746,8 +746,8 @@ type MapTypeExpr struct { Value Expr // value type } -func (x *MapTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *MapTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } type StructTypeExpr struct { @@ -755,8 +755,8 @@ type StructTypeExpr struct { Fields FieldTypeExprs // list of field declarations } -func (x *StructTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *StructTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } // Like ConstExpr but for types. @@ -766,8 +766,8 @@ type constTypeExpr struct { Type Type } -func (x *constTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *constTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } // Only used for native func arguments @@ -776,8 +776,8 @@ type MaybeNativeTypeExpr struct { Type Expr } -func (x *MaybeNativeTypeExpr) addressability() Addressability { - return AddressabilityNotApplicable +func (x *MaybeNativeTypeExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable } // ---------------------------------------- @@ -2263,3 +2263,11 @@ func validatePkgName(name string) { panic(fmt.Sprintf("cannot create package with invalid name %q", name)) } } + +type addressabilityStatus int + +const ( + addressabilityStatusNotApplicable addressabilityStatus = iota + addressabilityStatusSatisfied + addressabilityStatusUnsatisfied +) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 8dfcd70c9cc..b72b9c22c68 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1293,7 +1293,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { fv := cx.GetFunc() if fv.PkgPath == uversePkgPath && fv.Name == "append" { // append returns a slice and slices are always addressable. - n.Addressability = AddressabilitySatisfied + n.Addressability = addressabilityStatusSatisfied if n.Varg && len(n.Args) == 2 { // If the second argument is a string, // convert to byteslice. @@ -1343,12 +1343,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } else if fv.PkgPath == uversePkgPath && fv.Name == "new" { // The pointer value returned is not addressable, but maybe some selector // will make it addressable. For now mark it as not addressable. - n.Addressability = AddressabilityUnsatisfied + n.Addressability = addressabilityStatusUnsatisfied } } - if n.Addressability != AddressabilitySatisfied && len(ft.Results) == 1 { - n.Addressability = AddressabilityUnsatisfied + if n.Addressability != addressabilityStatusSatisfied && len(ft.Results) == 1 { + n.Addressability = addressabilityStatusUnsatisfied } // Continue with general case. @@ -1490,7 +1490,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { dt = dt.Elem() n.X = &StarExpr{X: n.X} n.X.SetAttribute(ATTR_PREPROCESSED, true) - n.Addressability = AddressabilitySatisfied + n.Addressability = addressabilityStatusSatisfied } switch dt.Kind() { case StringKind, ArrayKind, SliceKind: @@ -1499,15 +1499,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { checkOrConvertIntegerKind(store, last, n.Index) if dt.Kind() == SliceKind { // A string index is not addressable. - n.Addressability = AddressabilitySatisfied + n.Addressability = addressabilityStatusSatisfied } else if dt.Kind() == StringKind { // Special case; string indexes are never addressable. - n.Addressability = AddressabilityUnsatisfied + n.Addressability = addressabilityStatusUnsatisfied } case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) checkOrConvertType(store, last, &n.Index, mt.Key, false) - n.Addressability = AddressabilityUnsatisfied + n.Addressability = addressabilityStatusUnsatisfied default: panic(fmt.Sprintf( "unexpected index base kind for type %s", @@ -1524,7 +1524,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { t := evalStaticTypeOf(store, last, n.X) if t.Kind() == ArrayKind { - if n.X.addressability() == AddressabilityUnsatisfied { + if n.X.addressability() == addressabilityStatusUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } } @@ -2400,7 +2400,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Type = constType(n.Type, dst) case *RefExpr: - if n.X.addressability() == AddressabilityUnsatisfied { + if n.X.addressability() == addressabilityStatusUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } } From 804c17e0d7d15e459ac2430fb80d585545780903 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 19 Sep 2024 13:35:22 -1000 Subject: [PATCH 28/40] remove assertExpr --- gnovm/pkg/gnolang/nodes.go | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 2779a4d4e8a..1d5ca158d35 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -327,29 +327,11 @@ var ( type Expr interface { Node - assertExpr() addressability() addressabilityStatus } type Exprs []Expr -// non-pointer receiver to help make immutable. -func (*NameExpr) assertExpr() {} -func (*BasicLitExpr) assertExpr() {} -func (*BinaryExpr) assertExpr() {} -func (*CallExpr) assertExpr() {} -func (*IndexExpr) assertExpr() {} -func (*SelectorExpr) assertExpr() {} -func (*SliceExpr) assertExpr() {} -func (*StarExpr) assertExpr() {} -func (*RefExpr) assertExpr() {} -func (*TypeAssertExpr) assertExpr() {} -func (*UnaryExpr) assertExpr() {} -func (*CompositeLitExpr) assertExpr() {} -func (*KeyValueExpr) assertExpr() {} -func (*FuncLitExpr) assertExpr() {} -func (*ConstExpr) assertExpr() {} - var ( _ Expr = &NameExpr{} _ Expr = &BasicLitExpr{} @@ -623,17 +605,6 @@ func (x *StructTypeExpr) assertTypeExpr() {} func (x *constTypeExpr) assertTypeExpr() {} func (x *MaybeNativeTypeExpr) assertTypeExpr() {} -func (x *FieldTypeExpr) assertExpr() {} -func (x *ArrayTypeExpr) assertExpr() {} -func (x *SliceTypeExpr) assertExpr() {} -func (x *InterfaceTypeExpr) assertExpr() {} -func (x *ChanTypeExpr) assertExpr() {} -func (x *FuncTypeExpr) assertExpr() {} -func (x *MapTypeExpr) assertExpr() {} -func (x *StructTypeExpr) assertExpr() {} -func (x *constTypeExpr) assertExpr() {} -func (x *MaybeNativeTypeExpr) assertExpr() {} - var ( _ TypeExpr = &FieldTypeExpr{} _ TypeExpr = &ArrayTypeExpr{} From 44cc298b03619832255641603d5575d94979fcae Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 19 Sep 2024 13:45:06 -1000 Subject: [PATCH 29/40] clarifying comment --- gnovm/pkg/gnolang/nodes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 1d5ca158d35..0dc8b4ee2cc 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -409,7 +409,7 @@ type IndexExpr struct { // X[Index] } func (x *IndexExpr) addressability() addressabilityStatus { - // In this case NotApplicable means that it wasn't set, the default value. + // If not set in TRANS_LEAVE, defer to the the child expression's addressability. if x.Addressability == addressabilityStatusNotApplicable { return x.X.addressability() } From 548af8704c5227d8e8a077200d6c190950c12cd3 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 19 Sep 2024 13:52:25 -1000 Subject: [PATCH 30/40] fixed comment --- gnovm/pkg/gnolang/preprocess.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index b72b9c22c68..4ed41b6eec7 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1498,7 +1498,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // or if not const, assert integer type.. checkOrConvertIntegerKind(store, last, n.Index) if dt.Kind() == SliceKind { - // A string index is not addressable. + // A value at a slice index is always addressable because the underlying + // array is addressable. n.Addressability = addressabilityStatusSatisfied } else if dt.Kind() == StringKind { // Special case; string indexes are never addressable. From 8bec242b7beacc6d3bb4f658255390671fadf505 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 15:59:53 -1000 Subject: [PATCH 31/40] panic when calling addressability on type expressions --- gnovm/pkg/gnolang/nodes.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 0dc8b4ee2cc..2a6c1bc66c6 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -593,6 +593,8 @@ type TypeExpr interface { assertTypeExpr() } +const typeExprAddressability = "the addressability method should not be called on Type Expressions" + // non-pointer receiver to help make immutable. func (x *FieldTypeExpr) assertTypeExpr() {} func (x *ArrayTypeExpr) assertTypeExpr() {} @@ -629,7 +631,7 @@ type FieldTypeExpr struct { } func (x *FieldTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type FieldTypeExprs []FieldTypeExpr @@ -657,7 +659,7 @@ type ArrayTypeExpr struct { } func (x *ArrayTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type SliceTypeExpr struct { @@ -667,7 +669,7 @@ type SliceTypeExpr struct { } func (x *SliceTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type InterfaceTypeExpr struct { @@ -677,7 +679,7 @@ type InterfaceTypeExpr struct { } func (x *InterfaceTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type ChanDir int @@ -698,7 +700,7 @@ type ChanTypeExpr struct { } func (x *ChanTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type FuncTypeExpr struct { @@ -708,7 +710,7 @@ type FuncTypeExpr struct { } func (x *FuncTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type MapTypeExpr struct { @@ -718,7 +720,7 @@ type MapTypeExpr struct { } func (x *MapTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } type StructTypeExpr struct { @@ -727,7 +729,7 @@ type StructTypeExpr struct { } func (x *StructTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } // Like ConstExpr but for types. @@ -738,7 +740,7 @@ type constTypeExpr struct { } func (x *constTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } // Only used for native func arguments @@ -748,7 +750,7 @@ type MaybeNativeTypeExpr struct { } func (x *MaybeNativeTypeExpr) addressability() addressabilityStatus { - return addressabilityStatusNotApplicable + panic(typeExprAddressability) } // ---------------------------------------- From 8fe3b37442b5f073899d0ce1890fea8cd92b1008 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 16:08:56 -1000 Subject: [PATCH 32/40] fixed tesst --- gnovm/tests/files/addressable_7b_err.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/addressable_7b_err.gno b/gnovm/tests/files/addressable_7b_err.gno index 133d6f3af8b..a621d688ea4 100644 --- a/gnovm/tests/files/addressable_7b_err.gno +++ b/gnovm/tests/files/addressable_7b_err.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/addressable_7c_err.gno:4:9: cannot take address of (const (9 int)) +// main/files/addressable_7b_err.gno:4:9: cannot take address of (const (9 int)) From 26e8529c67cd846318c354ed8af1e4ca2bd25e9b Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 16:09:07 -1000 Subject: [PATCH 33/40] added comment --- gnovm/pkg/gnolang/preprocess.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 4ed41b6eec7..312aa929ba6 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1497,6 +1497,11 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Replace const index with int *ConstExpr, // or if not const, assert integer type.. checkOrConvertIntegerKind(store, last, n.Index) + + // Addressability of this index expression can only be known for slice and + // strings, explanations below in the respective blocks. If this is an index + // on an array, do nothing. This will defer to the array's addresability when + // the `addressability` method is called on this index expression. if dt.Kind() == SliceKind { // A value at a slice index is always addressable because the underlying // array is addressable. @@ -2396,7 +2401,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // We need to replace all references of the new // Type with old Type, including in attributes. n.Type.SetAttribute(ATTR_TYPE_VALUE, dst) - // Replace the type with *constTypeExpr{}, + // Replace the type with *{}, // otherwise methods would be un at runtime. n.Type = constType(n.Type, dst) From feee13de1a6a74f1fda7dedd670230991e3dd72d Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 16:43:44 -1000 Subject: [PATCH 34/40] fixed test --- gnovm/tests/files/addressable_8.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/addressable_8.gno b/gnovm/tests/files/addressable_8.gno index b28c4292661..3fde1018185 100644 --- a/gnovm/tests/files/addressable_8.gno +++ b/gnovm/tests/files/addressable_8.gno @@ -6,4 +6,4 @@ func main() { } // Output: -// &( func()()) +// &(func()(){...} func()()) From b33f0b0b284299b704ea7a4dade76c31e87d743a Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 17:13:52 -1000 Subject: [PATCH 35/40] fix double reference addressability and add comments --- gnovm/pkg/gnolang/preprocess.go | 11 ++++++++++- gnovm/tests/files/addressable_3d_err.gno | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 gnovm/tests/files/addressable_3d_err.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 5c1b252c93e..bd47984207f 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1706,6 +1706,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } + // If ftype is TRANS_REF_X, then this composite literal being created looks + // something like this in the code: `&MyStruct{}`. It is marked as addressable here + // because on TRANS_LEAVE for a RefExpr, it defers to the addressability of the + // expression it is referencing. When a composite literal is created with a preceding + // '&', it means the value is assigned to an address and that address is returned, + // so the value is addressable. if ftype == TRANS_REF_X { n.IsAddressable = true } @@ -2437,7 +2443,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Type = constType(n.Type, dst) case *RefExpr: - if n.X.addressability() == addressabilityStatusUnsatisfied { + // If ftype is TRANS_REF_X, then this expression is something like: + // &(&value). The resulting pointer value of the first reference is not + // addressable. Otherwise fall back to the target expression's addressability. + if ftype == TRANS_REF_X || n.X.addressability() == addressabilityStatusUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } } diff --git a/gnovm/tests/files/addressable_3d_err.gno b/gnovm/tests/files/addressable_3d_err.gno new file mode 100644 index 00000000000..18b7053d285 --- /dev/null +++ b/gnovm/tests/files/addressable_3d_err.gno @@ -0,0 +1,9 @@ +package main + +func main() { + _ = &(&struct{}{}) +} + +// Error: +// main/files/addressable_3d_err.gno:4:7: cannot take address of struct { }{} +// *** CHECK THE ERR MESSAGES ABOVE, MAKE SURE IT'S WHAT YOU EXPECTED, DELETE THIS LINE AND RUN TEST AGAIN *** From a8827ffeb1abce50b3787ff877dbd0da6f8d69bf Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 17:18:07 -1000 Subject: [PATCH 36/40] fixed test --- gnovm/tests/files/addressable_3d_err.gno | 1 - 1 file changed, 1 deletion(-) diff --git a/gnovm/tests/files/addressable_3d_err.gno b/gnovm/tests/files/addressable_3d_err.gno index 18b7053d285..d08c3a36e7e 100644 --- a/gnovm/tests/files/addressable_3d_err.gno +++ b/gnovm/tests/files/addressable_3d_err.gno @@ -6,4 +6,3 @@ func main() { // Error: // main/files/addressable_3d_err.gno:4:7: cannot take address of struct { }{} -// *** CHECK THE ERR MESSAGES ABOVE, MAKE SURE IT'S WHAT YOU EXPECTED, DELETE THIS LINE AND RUN TEST AGAIN *** From 71afb10ef07a506079180955b32e41c22627c3b5 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 20 Sep 2024 17:22:46 -1000 Subject: [PATCH 37/40] corrected double ref addressability check --- gnovm/pkg/gnolang/preprocess.go | 5 +++-- gnovm/tests/files/addressable_3d_err.gno | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index bd47984207f..86e3fbcb595 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2443,10 +2443,11 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Type = constType(n.Type, dst) case *RefExpr: - // If ftype is TRANS_REF_X, then this expression is something like: + // If n.X is a RefExpr, then this expression is something like: // &(&value). The resulting pointer value of the first reference is not // addressable. Otherwise fall back to the target expression's addressability. - if ftype == TRANS_REF_X || n.X.addressability() == addressabilityStatusUnsatisfied { + _, xIsRef := n.X.(*RefExpr) + if xIsRef || n.X.addressability() == addressabilityStatusUnsatisfied { panic(fmt.Sprintf("cannot take address of %s", n.X.String())) } } diff --git a/gnovm/tests/files/addressable_3d_err.gno b/gnovm/tests/files/addressable_3d_err.gno index d08c3a36e7e..00d75f80402 100644 --- a/gnovm/tests/files/addressable_3d_err.gno +++ b/gnovm/tests/files/addressable_3d_err.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/addressable_3d_err.gno:4:7: cannot take address of struct { }{} +// main/files/addressable_3d_err.gno:4:6: cannot take address of &(struct { }{}) From 2fc5c888165301455efeced5642e30f94ffcbc23 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 23 Sep 2024 14:16:30 -0700 Subject: [PATCH 38/40] fixed star expression addressability --- gnovm/pkg/gnolang/nodes.go | 2 +- gnovm/pkg/gnolang/preprocess.go | 1 - gnovm/tests/files/addressable_11.gno | 39 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 gnovm/tests/files/addressable_11.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 2a6c1bc66c6..4ee08381986 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -454,7 +454,7 @@ type StarExpr struct { // *X } func (x *StarExpr) addressability() addressabilityStatus { - return x.X.addressability() + return addressabilityStatusSatisfied } type RefExpr struct { // &X diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 86e3fbcb595..bcac276a090 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1521,7 +1521,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { dt = dt.Elem() n.X = &StarExpr{X: n.X} n.X.SetAttribute(ATTR_PREPROCESSED, true) - n.Addressability = addressabilityStatusSatisfied } switch dt.Kind() { case StringKind, ArrayKind, SliceKind: diff --git a/gnovm/tests/files/addressable_11.gno b/gnovm/tests/files/addressable_11.gno new file mode 100644 index 00000000000..607c155abfe --- /dev/null +++ b/gnovm/tests/files/addressable_11.gno @@ -0,0 +1,39 @@ +package main + +func main() { + var ii **int + i := new(int) + ii = &i + println(&(*ii)) + println(&ii) + println(i) + println(ii) + println(&i) + + j := new(int) + println(&(*j)) + + println(&(*getPtr())) + + derefTypeAssert() +} + +func getPtr() *int { + return new(int) +} + +func derefTypeAssert() { + var i interface{} + i = new(int) + println(&(*(i.(*int)))) +} + +// Output: +// &(&(0 int) *int) +// &(&(&(0 int) *int) **int) +// &(0 int) +// &(&(0 int) *int) +// &(&(0 int) *int) +// &(0 int) +// &(0 int) +// &(0 int) From eddcd54ee66b787424571a7ba58201880c223600 Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 25 Sep 2024 10:07:38 -0700 Subject: [PATCH 39/40] added comment --- gnovm/pkg/gnolang/preprocess.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index bcac276a090..0920e97aa2c 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1378,6 +1378,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } + // If addressability is not satisfied at this point and the function call returns only one + // result, then mark addressability as unsatisfied. Otherwise, this expression has already + // been explicitly marked as satisfied, or the function returns multiple results, rendering + // addressability NotApplicable for this situation -- it should fallback to the error produced + // when trying to take a reference or slice the result of a call expression that returns + // multiple values. if n.Addressability != addressabilityStatusSatisfied && len(ft.Results) == 1 { n.Addressability = addressabilityStatusUnsatisfied } From b3d9f9aa1dd3f57e2a5cbcaedacf0d1875cd76fc Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 7 Oct 2024 22:09:09 +0100 Subject: [PATCH 40/40] Update gnovm/pkg/gnolang/nodes.go Co-authored-by: ltzmaxwell --- gnovm/pkg/gnolang/nodes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 4ee08381986..ab9a4b316f8 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1771,7 +1771,7 @@ func (sb *StaticBlock) GetIsConst(store Store, n Name) bool { func (sb *StaticBlock) getAt(store Store, path ValuePath) *StaticBlock { if debug { if path.Type != VPBlock { - panic("should not happen") + panic("expected block type value path but got " + path.Type.String()) } if path.Depth == 0 { panic("should not happen")