Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: addressability #2731

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c8462ec
preprocessor addressability work
deelawn Aug 27, 2024
ef97274
fix selector isaddressable
deelawn Aug 27, 2024
7fa9f50
fix starexpr isaddressable
deelawn Aug 27, 2024
08563c3
fix sliceexpr addressability
deelawn Aug 27, 2024
3b78ae9
fix typeassertexpr addressability
deelawn Aug 27, 2024
a0e13b9
fix callexpr addressable return types
deelawn Aug 27, 2024
ae55b84
mark append and new results as addressable
deelawn Aug 27, 2024
990017d
fix selector addressability
deelawn Aug 27, 2024
3621dc7
make string indexes non-addressable
deelawn Aug 28, 2024
9faf3df
addressability file tests
deelawn Aug 28, 2024
8671ea5
prohibit taking address of imported typed constant
deelawn Aug 28, 2024
816dbd6
more tests
deelawn Aug 28, 2024
77caea9
fix type assertion addressability and add tests
deelawn Aug 29, 2024
89a041c
make sure to use base types when considering addressability
deelawn Aug 29, 2024
9956c26
move tests
deelawn Aug 29, 2024
6940714
fix index and selector addressability
deelawn Aug 29, 2024
3f4f1b6
introduce concept of addressability not applicable
deelawn Aug 29, 2024
c16e5e9
fixed test
deelawn Aug 29, 2024
ac79f00
fixed test
deelawn Aug 29, 2024
df764cd
fix test
deelawn Aug 30, 2024
af12ca1
func lit tests
deelawn Aug 30, 2024
e498f1c
index, map, and call expr fixes and tests
deelawn Aug 30, 2024
bee868c
addressability consistentcy fixes
deelawn Aug 30, 2024
9698461
consolidate simplest block path traversal
deelawn Sep 4, 2024
b284b56
fix comment
deelawn Sep 6, 2024
dfe024f
Removed test and renamed other
deelawn Sep 12, 2024
0e80801
move and rename addressability constants
deelawn Sep 19, 2024
804c17e
remove assertExpr
deelawn Sep 19, 2024
44cc298
clarifying comment
deelawn Sep 19, 2024
548af87
fixed comment
deelawn Sep 19, 2024
8bec242
panic when calling addressability on type expressions
deelawn Sep 21, 2024
8fe3b37
fixed tesst
deelawn Sep 21, 2024
26e8529
added comment
deelawn Sep 21, 2024
3c120ae
Merge branch 'master' into fix/addressability
deelawn Sep 21, 2024
feee13d
fixed test
deelawn Sep 21, 2024
b33f0b0
fix double reference addressability and add comments
deelawn Sep 21, 2024
a8827ff
fixed test
deelawn Sep 21, 2024
71afb10
corrected double ref addressability check
deelawn Sep 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions gnovm/pkg/gnolang/addressability.go
Copy link
Member

Choose a reason for hiding this comment

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

This should just be in nodes.go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ideally nodes.go would b less than 2200 LOC, but I've moved this back here as you suggested since moving a single concept into a new file that is closely related to this may be premature; a larger reorganization is necessary here. 0e80801

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gnolang

type Addressability int

const (
AddressabilityNotApplicable Addressability = iota
AddressabilitySatisfied
AddressabilityUnsatisfied
)
165 changes: 150 additions & 15 deletions gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@
type Expr interface {
Node
assertExpr()
addressability() Addressability
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
addressability() Addressability
Addressable() Addressability

If Addressability is exported, so should the method to get it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Neither should really be exported; I was just running short on names. I renamed it so this is no longer an issue. 0e80801

}

type Exprs []Expr
Expand Down Expand Up @@ -374,6 +375,10 @@
Name
}

func (x *NameExpr) addressability() Addressability {
Copy link
Member

Choose a reason for hiding this comment

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

I've been thinking about what's the best approach here.

Part of me would prefer if this was just a isAddressable if statement with a big type switch. However, I think there's a good point in your approach:

  • There's many expressions where addressability depends on the "children" expressions; having them as fields allow us to memoize in Preprocess what their result is.
  • Having this method as a requirement of the Expr interface means all Expr types must implement it, avoiding us to have to write a should-not-happen kind of panic.

But I do think we should concentrate the rules for addressability in some code packed tightly together, so it can be easily cross-checked with the Go reference in hand.

So what do you say about moving all of these methods underneath the block of assertExpr methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I was able to do even one better here and remove the assertExpr method entirely. Now that Expr has a meaningful method, addressability, there is no need for an extra method just to satisfy the interface. Given that change, let's keep the method implementations with each of the types; it is the natural place where one would expect them to be. 804c17e

return AddressabilitySatisfied
}

type NameExprs []NameExpr

type BasicLitExpr struct {
Expand All @@ -385,33 +390,65 @@
Value string
}

func (x *BasicLitExpr) addressability() Addressability {
return AddressabilityUnsatisfied

Check warning on line 394 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L393-L394

Added lines #L393 - L394 were not covered by tests
}

type BinaryExpr struct { // (Left Op Right)
Attributes
Left Expr // left operand
Op Word // operator
Right Expr // right operand
}

func (x *BinaryExpr) addressability() Addressability {
return AddressabilityUnsatisfied

Check warning on line 405 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L404-L405

Added lines #L404 - L405 were not covered by tests
}

type CallExpr struct { // Func(Args<Varg?...>)
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 Addressability
Copy link
Member

Choose a reason for hiding this comment

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

For the cases like this where the addressability is assigned during preprocess, what do you think about moving Addressability as an attribute?

The reason why I say this is that we currently have a bunch of information which is not actually persisted, but recovered by using Preprocess. The Expr types here contain in their struct only information which comes directly out of the AST representation; not from preprocessing. I'm not entirely sure of why it's designed this way; but I think for consistency we should maintain the split between "preprocessed information" in Attributes, and AST information in the struct themselves.

If addressability is an attribute, I also wonder if there's a way to remove the specific type and turn this into Addressable() bool instead.

}

func (x *CallExpr) addressability() Addressability {
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>[<Key>]
X Expr // expression
Index Expr // index expression
HasOK bool // if true, is form: `value, ok := <X>[<Key>]
Addressability Addressability
}

func (x *IndexExpr) addressability() Addressability {
// In this case NotApplicable means that it wasn't set, the default value.
if x.Addressability == AddressabilityNotApplicable {
Copy link
Member

Choose a reason for hiding this comment

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

When does this happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This happens when it is not set in TRANS_LEAVE, here

if dt.Kind() == SliceKind {
// A string index is not addressable.
n.Addressability = addressabilityStatusSatisfied
} else if dt.Kind() == StringKind {
// Special case; string indexes are never addressable.
n.Addressability = addressabilityStatusUnsatisfied
}

I've added a clarifying comment. 44cc298

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() Addressability {
if x.IsAddressable || x.X.addressability() == AddressabilitySatisfied {
return AddressabilitySatisfied
}

return AddressabilityUnsatisfied
}

type SliceExpr struct { // X[Low:High:Max]
Expand All @@ -422,6 +459,10 @@
Max Expr // maximum capacity of slice; or nil; added in Go 1.2
}

func (x *SliceExpr) addressability() Addressability {
return AddressabilityUnsatisfied
}

// A StarExpr node represents an expression of the form
// "*" Expression. Semantically it could be a unary "*"
// expression, or a pointer type.
Expand All @@ -430,16 +471,33 @@
X Expr // operand
}

func (x *StarExpr) addressability() Addressability {
return x.X.addressability()

Check warning on line 475 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L474-L475

Added lines #L474 - L475 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

Can you follow the code coverage annotations to add more test cases?

}

type RefExpr struct { // &X
Attributes
X Expr // operand
}

func (x *RefExpr) addressability() Addressability {
return x.X.addressability()

Check warning on line 484 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L483-L484

Added lines #L483 - L484 were not covered by tests
}

type TypeAssertExpr struct { // X.(Type)
Attributes
X Expr // expression.
Type Expr // asserted type, never nil.
HasOK bool // if true, is form: `_, ok := <X>.(<Type>)`.
X Expr // expression.
Type Expr // asserted type, never nil.
HasOK bool // if true, is form: `_, ok := <X>.(<Type>)`.
IsAddressable bool
}

func (x *TypeAssertExpr) addressability() Addressability {
if x.IsAddressable {
return AddressabilitySatisfied

Check warning on line 497 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L497

Added line #L497 was not covered by tests
}

return AddressabilityUnsatisfied
}

// A UnaryExpr node represents a unary expression. Unary
Expand All @@ -452,12 +510,25 @@
Op Word // operator
}

func (x *UnaryExpr) addressability() Addressability {
return x.X.addressability()

Check warning on line 514 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L513-L514

Added lines #L513 - L514 were not covered by tests
}

// MyType{<key>:<value>} 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() Addressability {
if x.IsAddressable {
return AddressabilitySatisfied
}

return AddressabilityUnsatisfied
}

// Returns true if any elements are keyed.
Expand Down Expand Up @@ -490,6 +561,10 @@
Value Expr // never nil
}

func (x *KeyValueExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 565 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L564-L565

Added lines #L564 - L565 were not covered by tests
}

type KeyValueExprs []KeyValueExpr

// A FuncLitExpr node represents a function literal. Here one
Expand All @@ -502,6 +577,10 @@
Body // function body
}

func (x *FuncLitExpr) addressability() Addressability {
return AddressabilityUnsatisfied
}

// The preprocessor replaces const expressions
// with *ConstExpr nodes.
type ConstExpr struct {
Expand All @@ -510,6 +589,10 @@
TypedValue
}

func (x *ConstExpr) addressability() Addressability {
return AddressabilityUnsatisfied
}

// ----------------------------------------
// Type(Expressions)
//
Expand Down Expand Up @@ -574,6 +657,10 @@
Tag Expr
}

func (x *FieldTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 661 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L660-L661

Added lines #L660 - L661 were not covered by tests
}

type FieldTypeExprs []FieldTypeExpr

func (ftxz FieldTypeExprs) IsNamed() bool {
Expand All @@ -598,18 +685,30 @@
Elt Expr // element type
}

func (x *ArrayTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 689 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L688-L689

Added lines #L688 - L689 were not covered by tests
}

type SliceTypeExpr struct {
Attributes
Elt Expr // element type
Vrd bool // variadic arg expression
}

func (x *SliceTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 699 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L698-L699

Added lines #L698 - L699 were not covered by tests
}

type InterfaceTypeExpr struct {
Attributes
Methods FieldTypeExprs // list of methods
Generic Name // for uverse generics
}

func (x *InterfaceTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 709 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L708-L709

Added lines #L708 - L709 were not covered by tests
}

type ChanDir int

const (
Expand All @@ -627,36 +726,60 @@
Value Expr // value type
}

func (x *ChanTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 730 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L729-L730

Added lines #L729 - L730 were not covered by tests
}

type FuncTypeExpr struct {
Attributes
Params FieldTypeExprs // (incoming) parameters, if any.
Results FieldTypeExprs // (outgoing) results, if any.
}

func (x *FuncTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 740 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L739-L740

Added lines #L739 - L740 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

For these cases where we just return NotApplicable, I wonder if we should just panic...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the case of type expressions, yes, we should panic because it is unexpected to have this method called 8bec242

But we shouldn't get rid of NotApplicable altogether. There are some valid cases, for example, when there is code like this:

func main() {
    _ = &DoSomething()
}

func DoSomething() (int, int) { ... }

In this case addressability is not applicable because it should fail with an error message referencing the fact that it is trying to take a reference, which requires one operand, on the result of a function that returns multiple values, so we don't want to trigger an addressability error here. In Go, this would panic with something like multiple-value DoSomething() (value of type (int, int)) in single-value context.

}

type MapTypeExpr struct {
Attributes
Key Expr // const
Value Expr // value type
}

func (x *MapTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 750 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L749-L750

Added lines #L749 - L750 were not covered by tests
}

type StructTypeExpr struct {
Attributes
Fields FieldTypeExprs // list of field declarations
}

func (x *StructTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 759 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L758-L759

Added lines #L758 - L759 were not covered by tests
}

// Like ConstExpr but for types.
type constTypeExpr struct {
Attributes
Source Expr
Type Type
}

func (x *constTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 770 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L769-L770

Added lines #L769 - L770 were not covered by tests
}

// Only used for native func arguments
type MaybeNativeTypeExpr struct {
Attributes
Type Expr
}

func (x *MaybeNativeTypeExpr) addressability() Addressability {
return AddressabilityNotApplicable

Check warning on line 780 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L779-L780

Added lines #L779 - L780 were not covered by tests
}

// ----------------------------------------
// Stmt
//
Expand Down Expand Up @@ -1484,6 +1607,7 @@
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
Expand Down Expand Up @@ -1671,6 +1795,17 @@
}
}

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()
thehowl marked this conversation as resolved.
Show resolved Hide resolved
path.Depth -= 1

Check warning on line 1804 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1803-L1804

Added lines #L1803 - L1804 were not covered by tests
}
}
}

// Returns true iff n is a local const defined name.
func (sb *StaticBlock) getLocalIsConst(n Name) bool {
for _, name := range sb.Consts {
Expand Down
Loading
Loading