From c8462ec305096ce483b455327bba077fa7ffe9f6 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 26 Aug 2024 17:20:10 -0700 Subject: [PATCH] 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 -----------------------