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 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 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
2fc5c88
fixed star expression addressability
deelawn Sep 23, 2024
eddcd54
added comment
deelawn Sep 25, 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
234 changes: 174 additions & 60 deletions gnovm/pkg/gnolang/nodes.go

Large diffs are not rendered by default.

68 changes: 63 additions & 5 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -1369,9 +1371,17 @@ 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 n.Addressability != addressabilityStatusSatisfied && len(ft.Results) == 1 {
Copy link
Contributor

Choose a reason for hiding this comment

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

an explanatory comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a comment here eddcd54

n.Addressability = addressabilityStatusUnsatisfied
}

// Continue with general case.
hasVarg := ft.HasVarg()
isVarg := n.Varg
Expand Down Expand Up @@ -1517,9 +1527,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",
Expand All @@ -1534,8 +1558,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 {
ltzmaxwell marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand Down Expand Up @@ -1573,8 +1603,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)
Expand Down Expand Up @@ -1634,6 +1662,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)
Expand Down Expand Up @@ -1673,6 +1705,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 {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
n.IsAddressable = true
}

// TRANS_LEAVE -----------------------
case *KeyValueExpr:
// NOTE: For simplicity we just
Expand All @@ -1681,6 +1723,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) {
Expand All @@ -1693,6 +1738,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 {
Expand Down Expand Up @@ -1795,10 +1841,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)
Expand Down Expand Up @@ -2388,9 +2437,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 -----------------------
Expand Down
3 changes: 2 additions & 1 deletion gnovm/stdlibs/crypto/sha256/sha256_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
38 changes: 38 additions & 0 deletions gnovm/tests/files/addressable_1.gno
Original file line number Diff line number Diff line change
@@ -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)]
12 changes: 12 additions & 0 deletions gnovm/tests/files/addressable_10.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

type S struct {
i int
}

func main() {
println(&new(S).i)
}

// Output:
// &(0 int)
14 changes: 14 additions & 0 deletions gnovm/tests/files/addressable_10a_err.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,2)>()
12 changes: 12 additions & 0 deletions gnovm/tests/files/addressable_10b_err.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,0)>)
39 changes: 39 additions & 0 deletions gnovm/tests/files/addressable_11.gno
Original file line number Diff line number Diff line change
@@ -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)
8 changes: 8 additions & 0 deletions gnovm/tests/files/addressable_1a_err.gno
Original file line number Diff line number Diff line change
@@ -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))]
8 changes: 8 additions & 0 deletions gnovm/tests/files/addressable_1b_err.gno
Original file line number Diff line number Diff line change
@@ -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))}
13 changes: 13 additions & 0 deletions gnovm/tests/files/addressable_1c_err.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,1)>()[(const (0 int))]
12 changes: 12 additions & 0 deletions gnovm/tests/files/addressable_1d_err.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,1)>()
40 changes: 40 additions & 0 deletions gnovm/tests/files/addressable_2.gno
Original file line number Diff line number Diff line change
@@ -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)]
8 changes: 8 additions & 0 deletions gnovm/tests/files/addressable_2a_err.gno
Original file line number Diff line number Diff line change
@@ -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))}[:]
12 changes: 12 additions & 0 deletions gnovm/tests/files/addressable_2b_err.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,1)>()
Loading
Loading