diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index b18ed157ca6..ab9a4b316f8 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -327,28 +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{} @@ -374,6 +357,10 @@ type NameExpr struct { Name } +func (x *NameExpr) addressability() addressabilityStatus { + return addressabilityStatusSatisfied +} + type NameExprs []NameExpr type BasicLitExpr struct { @@ -385,6 +372,10 @@ type BasicLitExpr struct { Value string } +func (x *BasicLitExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied +} + type BinaryExpr struct { // (Left Op Right) Attributes Left Expr // left operand @@ -392,26 +383,54 @@ type BinaryExpr struct { // (Left Op Right) Right Expr // right operand } +func (x *BinaryExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied +} + 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) + Addressability addressabilityStatus +} + +func (x *CallExpr) addressability() addressabilityStatus { + return x.Addressability } 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 := [] + Addressability addressabilityStatus +} + +func (x *IndexExpr) addressability() addressabilityStatus { + // If not set in TRANS_LEAVE, defer to the the child expression's addressability. + if x.Addressability == addressabilityStatusNotApplicable { + return x.X.addressability() + } + + return x.Addressability } 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 // true if X is a pointer +} + +func (x *SelectorExpr) addressability() addressabilityStatus { + if x.IsAddressable || x.X.addressability() == addressabilityStatusSatisfied { + return addressabilityStatusSatisfied + } + + return addressabilityStatusUnsatisfied } type SliceExpr struct { // X[Low:High:Max] @@ -422,6 +441,10 @@ type SliceExpr struct { // X[Low:High:Max] Max Expr // maximum capacity of slice; or nil; added in Go 1.2 } +func (x *SliceExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied +} + // A StarExpr node represents an expression of the form // "*" Expression. Semantically it could be a unary "*" // expression, or a pointer type. @@ -430,16 +453,33 @@ type StarExpr struct { // *X X Expr // operand } +func (x *StarExpr) addressability() addressabilityStatus { + return addressabilityStatusSatisfied +} + type RefExpr struct { // &X Attributes X Expr // operand } +func (x *RefExpr) addressability() addressabilityStatus { + return x.X.addressability() +} + 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) addressability() addressabilityStatus { + if x.IsAddressable { + return addressabilityStatusSatisfied + } + + return addressabilityStatusUnsatisfied } // A UnaryExpr node represents a unary expression. Unary @@ -452,12 +492,25 @@ type UnaryExpr struct { // (Op X) Op Word // operator } +func (x *UnaryExpr) addressability() addressabilityStatus { + return x.X.addressability() +} + // 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) addressability() addressabilityStatus { + if x.IsAddressable { + return addressabilityStatusSatisfied + } + + return addressabilityStatusUnsatisfied } // Returns true if any elements are keyed. @@ -490,6 +543,10 @@ type KeyValueExpr struct { Value Expr // never nil } +func (x *KeyValueExpr) addressability() addressabilityStatus { + return addressabilityStatusNotApplicable +} + type KeyValueExprs []KeyValueExpr // A FuncLitExpr node represents a function literal. Here one @@ -502,6 +559,10 @@ type FuncLitExpr struct { Body // function body } +func (x *FuncLitExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied +} + // The preprocessor replaces const expressions // with *ConstExpr nodes. type ConstExpr struct { @@ -510,6 +571,10 @@ type ConstExpr struct { TypedValue } +func (x *ConstExpr) addressability() addressabilityStatus { + return addressabilityStatusUnsatisfied +} + // ---------------------------------------- // Type(Expressions) // @@ -528,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() {} @@ -540,17 +607,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{} @@ -574,6 +630,10 @@ type FieldTypeExpr struct { Tag Expr } +func (x *FieldTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type FieldTypeExprs []FieldTypeExpr func (ftxz FieldTypeExprs) IsNamed() bool { @@ -598,18 +658,30 @@ type ArrayTypeExpr struct { Elt Expr // element type } +func (x *ArrayTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type SliceTypeExpr struct { Attributes Elt Expr // element type Vrd bool // variadic arg expression } +func (x *SliceTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type InterfaceTypeExpr struct { Attributes Methods FieldTypeExprs // list of methods Generic Name // for uverse generics } +func (x *InterfaceTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type ChanDir int const ( @@ -627,23 +699,39 @@ type ChanTypeExpr struct { Value Expr // value type } +func (x *ChanTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type FuncTypeExpr struct { Attributes Params FieldTypeExprs // (incoming) parameters, if any. Results FieldTypeExprs // (outgoing) results, if any. } +func (x *FuncTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type MapTypeExpr struct { Attributes Key Expr // const Value Expr // value type } +func (x *MapTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + type StructTypeExpr struct { Attributes Fields FieldTypeExprs // list of field declarations } +func (x *StructTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + // Like ConstExpr but for types. type constTypeExpr struct { Attributes @@ -651,12 +739,20 @@ type constTypeExpr struct { Type Type } +func (x *constTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + // Only used for native func arguments type MaybeNativeTypeExpr struct { Attributes Type Expr } +func (x *MaybeNativeTypeExpr) addressability() addressabilityStatus { + panic(typeExprAddressability) +} + // ---------------------------------------- // Stmt // @@ -1484,6 +1580,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 @@ -1671,6 +1768,30 @@ 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("expected block type value path but got " + path.Type.String()) + } + if path.Depth == 0 { + panic("should not happen") + } + } + + for { + if path.Depth == 1 { + return sb + } else { + sb = sb.GetParentNode(store).GetStaticBlock() + path.Depth -= 1 + } + } +} + +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 { @@ -1706,22 +1827,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. @@ -2130,3 +2236,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 9168fc6f7c1..0920e97aa2c 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1323,6 +1323,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.Addressability = addressabilityStatusSatisfied if n.Varg && len(n.Args) == 2 { // If the second argument is a string, // convert to byteslice. @@ -1369,9 +1371,23 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Args[1] = args1 } } + } 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 = addressabilityStatusUnsatisfied } } + // 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 + } + // Continue with general case. hasVarg := ft.HasVarg() isVarg := n.Varg @@ -1517,9 +1533,23 @@ 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. + n.Addressability = addressabilityStatusSatisfied + } else if dt.Kind() == StringKind { + // Special case; string indexes are never addressable. + n.Addressability = addressabilityStatusUnsatisfied + } case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) checkOrConvertType(store, last, &n.Index, mt.Key, false) + n.Addressability = addressabilityStatusUnsatisfied default: panic(fmt.Sprintf( "unexpected index base kind for type %s", @@ -1534,8 +1564,14 @@ 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) + if t.Kind() == ArrayKind { + if n.X.addressability() == addressabilityStatusUnsatisfied { + panic(fmt.Sprintf("cannot take address of %s", n.X.String())) + } + } + + // if n.X is untyped, convert to corresponding type if isUntyped(t) { dt := defaultTypeOf(t) checkOrConvertType(store, last, &n.X, dt, false) @@ -1573,8 +1609,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ) } - evalStaticType(store, last, n.Type) - // TRANS_LEAVE ----------------------- case *UnaryExpr: xt := evalStaticTypeOf(store, last, n.X) @@ -1634,6 +1668,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) @@ -1673,6 +1711,16 @@ 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 + } + // TRANS_LEAVE ----------------------- case *KeyValueExpr: // NOTE: For simplicity we just @@ -1681,6 +1729,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *SelectorExpr: xt := evalStaticTypeOf(store, last, n.X) + if xt.Kind() == PointerKind { + n.IsAddressable = true + } // Set selector path based on xt's type. switch cxt := xt.(type) { @@ -1693,6 +1744,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { panic(fmt.Sprintf("missing field %s in %s", n.Sel, cxt.String())) } + if len(tr) > 1 { // (the last vp, tr[len(tr)-1], is for n.Sel) if debug { @@ -1795,10 +1847,13 @@ 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) { + + // 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 } + case *TypeType: // unbound method xt := evalStaticType(store, last, n.X) @@ -2388,9 +2443,18 @@ 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) + + case *RefExpr: + // 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. + _, xIsRef := n.X.(*RefExpr) + if xIsRef || n.X.addressability() == addressabilityStatusUnsatisfied { + panic(fmt.Sprintf("cannot take address of %s", n.X.String())) + } } // end type switch statement // END TRANS_LEAVE ----------------------- 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 { diff --git a/gnovm/tests/files/addressable_1.gno b/gnovm/tests/files/addressable_1.gno new file mode 100644 index 00000000000..30616145733 --- /dev/null +++ b/gnovm/tests/files/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/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_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) diff --git a/gnovm/tests/files/addressable_1a_err.gno b/gnovm/tests/files/addressable_1a_err.gno new file mode 100644 index 00000000000..a8c8c3b3c77 --- /dev/null +++ b/gnovm/tests/files/addressable_1a_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = &[1]int{1}[0] +} + +// Error: +// 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/files/addressable_1b_err.gno b/gnovm/tests/files/addressable_1b_err.gno new file mode 100644 index 00000000000..9d8a7d12e4b --- /dev/null +++ b/gnovm/tests/files/addressable_1b_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = [1]int{1}[:] +} + +// Error: +// 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/files/addressable_1c_err.gno b/gnovm/tests/files/addressable_1c_err.gno new file mode 100644 index 00000000000..420531187df --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_1c_err.gno:4:6: cannot take address of getArr()[(const (0 int))] diff --git a/gnovm/tests/files/addressable_1d_err.gno b/gnovm/tests/files/addressable_1d_err.gno new file mode 100644 index 00000000000..56bb81c881f --- /dev/null +++ b/gnovm/tests/files/addressable_1d_err.gno @@ -0,0 +1,12 @@ +package main + +func main() { + _ = getArr()[:] +} + +func getArr() [1]int { + return [1]int{1} +} + +// Error: +// main/files/addressable_1d_err.gno:4:6: cannot take address of getArr() diff --git a/gnovm/tests/files/addressable_2.gno b/gnovm/tests/files/addressable_2.gno new file mode 100644 index 00000000000..1baf7f29e65 --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_2a_err.gno b/gnovm/tests/files/addressable_2a_err.gno new file mode 100644 index 00000000000..363fa3d2817 --- /dev/null +++ b/gnovm/tests/files/addressable_2a_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = &[]int{1}[:] +} + +// Error: +// 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_3.gno b/gnovm/tests/files/addressable_3.gno new file mode 100644 index 00000000000..34e9a2e1edc --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_3a_err.gno b/gnovm/tests/files/addressable_3a_err.gno new file mode 100644 index 00000000000..dcf3883e79b --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_3a_err.gno:10:6: cannot take address of S{i: (const (4 int))}.i diff --git a/gnovm/tests/files/addressable_3b_err.gno b/gnovm/tests/files/addressable_3b_err.gno new file mode 100644 index 00000000000..db4e810155a --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_3b_err.gno:8:6: cannot take address of getStruct().i 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 diff --git a/gnovm/tests/files/addressable_3d_err.gno b/gnovm/tests/files/addressable_3d_err.gno new file mode 100644 index 00000000000..00d75f80402 --- /dev/null +++ b/gnovm/tests/files/addressable_3d_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _ = &(&struct{}{}) +} + +// Error: +// main/files/addressable_3d_err.gno:4:6: cannot take address of &(struct { }{}) diff --git a/gnovm/tests/files/addressable_4.gno b/gnovm/tests/files/addressable_4.gno new file mode 100644 index 00000000000..322cf8eabb3 --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_4a_err.gno b/gnovm/tests/files/addressable_4a_err.gno new file mode 100644 index 00000000000..17481976f3a --- /dev/null +++ b/gnovm/tests/files/addressable_4a_err.gno @@ -0,0 +1,9 @@ +package main + +func main() { + greeting := "hello" + _ = &greeting[2] +} + +// Error: +// main/files/addressable_4a_err.gno:5:6: cannot take address of greeting[(const (2 int))] diff --git a/gnovm/tests/files/addressable_5.gno b/gnovm/tests/files/addressable_5.gno new file mode 100644 index 00000000000..800cc744458 --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_5a_err_stdlibs.gno b/gnovm/tests/files/addressable_5a_err_stdlibs.gno new file mode 100644 index 00000000000..bc6318f511d --- /dev/null +++ b/gnovm/tests/files/addressable_5a_err_stdlibs.gno @@ -0,0 +1,11 @@ +package main + +import "math" + +func main() { + // Untyped constants are not addressable. + _ = &math.MaxUint8 +} + +// Error: +// main/files/addressable_5a_err_stdlibs.gno:7:6: cannot take address of (const (255 bigint)) diff --git a/gnovm/tests/files/addressable_5b_err_stdlibs.gno b/gnovm/tests/files/addressable_5b_err_stdlibs.gno new file mode 100644 index 00000000000..e2028a22c9e --- /dev/null +++ b/gnovm/tests/files/addressable_5b_err_stdlibs.gno @@ -0,0 +1,11 @@ +package main + +import "std" + +func main() { + // Type constants are not addressable. + _ = &std.BankerTypeReadonly +} + +// Error: +// main/files/addressable_5b_err_stdlibs.gno:7:6: cannot take address of (const (0 std.BankerType)) diff --git a/gnovm/tests/files/addressable_5c_err.gno b/gnovm/tests/files/addressable_5c_err.gno new file mode 100644 index 00000000000..92de1aeb30a --- /dev/null +++ b/gnovm/tests/files/addressable_5c_err.gno @@ -0,0 +1,10 @@ +package main + +const a = 1 + +func main() { + _ = &a +} + +// Error: +// main/files/addressable_5c_err.gno:6:6: cannot take address of (const (1 bigint)) diff --git a/gnovm/tests/files/addressable_5d_err.gno b/gnovm/tests/files/addressable_5d_err.gno new file mode 100644 index 00000000000..fe78ed36681 --- /dev/null +++ b/gnovm/tests/files/addressable_5d_err.gno @@ -0,0 +1,10 @@ +package main + +const a int = 1 + +func main() { + _ = &a +} + +// Error: +// main/files/addressable_5d_err.gno:6:6: cannot take address of (const (1 int)) diff --git a/gnovm/tests/files/addressable_6.gno b/gnovm/tests/files/addressable_6.gno new file mode 100644 index 00000000000..9fc0616f980 --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_6a_err.gno b/gnovm/tests/files/addressable_6a_err.gno new file mode 100644 index 00000000000..f27432fa81b --- /dev/null +++ b/gnovm/tests/files/addressable_6a_err.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var i interface{} + i = 5 + println(&i.(int)) +} + +// Error: +// main/files/addressable_6a_err.gno:6:10: cannot take address of i.((const-type int)) diff --git a/gnovm/tests/files/addressable_6b_err.gno b/gnovm/tests/files/addressable_6b_err.gno new file mode 100644 index 00000000000..ea2e19aa3c4 --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_6b_err.gno:10:10: cannot take address of i.(S).a diff --git a/gnovm/tests/files/addressable_6c_err.gno b/gnovm/tests/files/addressable_6c_err.gno new file mode 100644 index 00000000000..03666922eee --- /dev/null +++ b/gnovm/tests/files/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/files/addressable_6c_err.gno:6:10: cannot take address of i.([(const (1 int))](const-type int))[(const (0 int))] 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)) 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..a621d688ea4 --- /dev/null +++ b/gnovm/tests/files/addressable_7b_err.gno @@ -0,0 +1,8 @@ +package main + +func main() { + _, _ = &int(9) +} + +// Error: +// main/files/addressable_7b_err.gno:4:9: cannot take address of (const (9 int)) diff --git a/gnovm/tests/files/addressable_8.gno b/gnovm/tests/files/addressable_8.gno new file mode 100644 index 00000000000..3fde1018185 --- /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()(){...} 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))) } 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