Skip to content

Commit

Permalink
feat: add support for type declarations on pointer types (gnolang#1733)
Browse files Browse the repository at this point in the history
support type decl upon pointer type:

```go
package main

import "fmt"

// Define a base type
type Base int

// Declare a new type that is a pointer to the base type
type PtrToBase *Base

func main() {
	var b Base = 42      // Initialize a variable of the base type
	var p PtrToBase = &b // Initialize a variable of the new pointer type with the address of b

	fmt.Printf("The value of b is: %d\n", b)

	// Using the new pointer type
	fmt.Printf("The value pointed to by p is: %d\n", *p)

	// Modifying the value pointed to by p
	*p = 100
	fmt.Printf("The new value of b after modification through p is: %d\n", b)
}

// Output:
// The value of b is: 42
// The value pointed to by p is: 42
// The new value of b after modification through p is: 100
```
  • Loading branch information
ltzmaxwell committed Apr 10, 2024
1 parent 327d30e commit ca6566f
Show file tree
Hide file tree
Showing 18 changed files with 325 additions and 6 deletions.
2 changes: 1 addition & 1 deletion gnovm/pkg/gnolang/op_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (m *Machine) doOpStar() {
case *PointerType:
pv := xv.V.(PointerValue)
if pv.TV.T == DataByteType {
tv := TypedValue{T: xv.T.(*PointerType).Elt}
tv := TypedValue{T: bt.Elt}
dbv := pv.TV.V.(DataByteValue)
tv.SetUint8(dbv.GetByte())
m.PushValue(tv)
Expand Down
4 changes: 2 additions & 2 deletions gnovm/pkg/gnolang/op_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ func (m *Machine) doOpStaticTypeOf() {
m.PushOp(OpStaticTypeOf)
m.Run() // XXX replace
xt := m.ReapValues(start)[0].V.(TypeValue).Type
if pt, ok := xt.(*PointerType); ok {
if pt, ok := baseOf(xt).(*PointerType); ok {
m.PushValue(asValue(&SliceType{
Elt: pt.Elt.Elem(),
}))
Expand All @@ -485,7 +485,7 @@ func (m *Machine) doOpStaticTypeOf() {
m.PushOp(OpStaticTypeOf)
m.Run() // XXX replace
xt := m.ReapValues(start)[0].GetType()
if pt, ok := xt.(*PointerType); ok {
if pt, ok := baseOf(xt).(*PointerType); ok {
m.PushValue(asValue(pt.Elt))
} else if _, ok := xt.(*TypeType); ok {
m.PushValue(asValue(gTypeType))
Expand Down
29 changes: 26 additions & 3 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -2542,7 +2542,7 @@ func checkType(xt Type, dt Type, autoNative bool) {
// Special case if xt or dt is *PointerType to *NativeType,
// convert to *NativeType of pointer kind.
if pxt, ok := xt.(*PointerType); ok {
// *gonative{x} is gonative{*x}
// *gonative{x} is(to) gonative{*x}
//nolint:misspell
if enxt, ok := pxt.Elt.(*NativeType); ok {
xt = &NativeType{
Expand Down Expand Up @@ -3025,11 +3025,32 @@ func predefineNow2(store Store, last BlockNode, d Decl, m map[Name]struct{}) (De
ft := evalStaticType(store, last, &cd.Type).(*FuncType)
ft = ft.UnboundType(rft)
dt := (*DeclaredType)(nil)

// check base type of receiver type, should not be pointer type or interface type
assertValidReceiverType := func(t Type) {
if _, ok := t.(*PointerType); ok {
panic(fmt.Sprintf("invalid receiver type %v (base type is pointer type)\n", rt))
}
if _, ok := t.(*InterfaceType); ok {
panic(fmt.Sprintf("invalid receiver type %v (base type is interface type)\n", rt))
}
}

if pt, ok := rt.(*PointerType); ok {
dt = pt.Elem().(*DeclaredType)
assertValidReceiverType(pt.Elem())
if ddt, ok := pt.Elem().(*DeclaredType); ok {
assertValidReceiverType(baseOf(ddt))
dt = ddt
} else {
panic("should not happen")
}
} else if ddt, ok := rt.(*DeclaredType); ok {
assertValidReceiverType(baseOf(ddt))
dt = ddt
} else {
dt = rt.(*DeclaredType)
panic("should not happen")
}

if !dt.TryDefineMethod(&FuncValue{
Type: ft,
IsMethod: true,
Expand Down Expand Up @@ -3168,6 +3189,8 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) {
t = &MapType{}
case *StructTypeExpr:
t = &StructType{}
case *StarExpr:
t = &PointerType{}
case *NameExpr:
if tv := last.GetValueRef(store, tx.Name); tv != nil {
// (file) block name
Expand Down
28 changes: 28 additions & 0 deletions gnovm/tests/files/type33.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import "fmt"

// Define a base type
type Base int

// Declare a new type that is a pointer to the base type
type PtrToBase *Base

func main() {
var b Base = 42 // Initialize a variable of the base type
var p PtrToBase = &b // Initialize a variable of the new pointer type with the address of b

fmt.Printf("The value of b is: %d\n", b)

// Using the new pointer type
fmt.Printf("The value pointed to by p is: %d\n", *p)

// Modifying the value pointed to by p
*p = 100
fmt.Printf("The new value of b after modification through p is: %d\n", b)
}

// Output:
// The value of b is: 42
// The value pointed to by p is: 42
// The new value of b after modification through p is: 100
12 changes: 12 additions & 0 deletions gnovm/tests/files/type34.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

type BytePtr *byte

func main() {
bs := []byte("hello")
var p BytePtr = &bs[0]
println(*p)
}

// Output:
// 104
15 changes: 15 additions & 0 deletions gnovm/tests/files/type35.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

type IntPtr *int

func main() {
var a, b int
a = 1 // Set a to 104
s := []IntPtr{}
s = append(s, &a)
s = append(s, &b)
println(*s[0])
}

// Output:
// 1
17 changes: 17 additions & 0 deletions gnovm/tests/files/type36.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

type Arr [2]int
type Ptr *Arr

func main() {
var arr Arr
arr[0] = 0
arr[1] = 1

p := Ptr(&arr)

println(p[:])
}

// Output:
// slice[(0 int),(1 int)]
20 changes: 20 additions & 0 deletions gnovm/tests/files/type37.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import "fmt"

type IntArray []int
type Arr *IntArray

func (a Arr) Add(x int) { // receiver is val, not ptr
*a = append(*a, x)
}

func main() {
a := new(IntArray)
Arr(a).Add(4)

fmt.Println(*a)
}

// Error:
// main/files/type37.gno:8: invalid receiver type main.Arr (base type is pointer type)
19 changes: 19 additions & 0 deletions gnovm/tests/files/type37a.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import "fmt"

type IntArray []int

func (a *IntArray) Add(x int) { // receiver is val, not ptr
*a = append(*a, x)
}

func main() {
a := new(IntArray)
a.Add(4)

fmt.Println(*a)
}

// Output:
// [4]
22 changes: 22 additions & 0 deletions gnovm/tests/files/type37b.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import "fmt"

type Integer int

func (i **Integer) Add(x int) {
**i += Integer(x) // Dereference twice to get to the actual Integer value and modify it
}

func main() {
a := new(Integer) // a is a pointer to Integer
b := &a // b is a pointer to a pointer to Integer

// Since Add is defined on **Integer, you need to pass b
b.Add(4) // Adds 4 to the value **b points to

fmt.Println(**b) // Should print 4, as **b is the same as *a
}

// Error:
// main/files/type37b.gno:7: invalid receiver type **main.Integer (base type is pointer type)
20 changes: 20 additions & 0 deletions gnovm/tests/files/type37c.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import "fmt"

type Integer int

func (i *Integer) Add(x int) { // receiver is val, not ptr
println(int(*i) + x)
}

func main() {
a := new(Integer)
a.Add(4)

fmt.Println(*a)
}

// Output:
// 4
// 0
16 changes: 16 additions & 0 deletions gnovm/tests/files/type37d.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

type IntPtr *int

var ip IntPtr = new(int)

func (p IntPtr) Int() int {
return *p
}

func main() {
println(ip.Int())
}

// Error:
// main/files/type37d.gno:7: invalid receiver type main.IntPtr (base type is pointer type)
17 changes: 17 additions & 0 deletions gnovm/tests/files/type37e.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

type IntPtr *int
type Int2 IntPtr

var ip IntPtr = new(int)

func (i2 Int2) Int() int {
return *i2
}

func main() {
println(Int2(ip).Int())
}

// Error:
// main/files/type37e.gno:8: invalid receiver type main.Int2 (base type is pointer type)
16 changes: 16 additions & 0 deletions gnovm/tests/files/type37f.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

type IntPtr *int

var ip IntPtr = new(int)

func (p *IntPtr) Int() int {
return **p
}

func main() {
println((&ip).Int())
}

// Error:
// main/files/type37f.gno:7: invalid receiver type *main.IntPtr (base type is pointer type)
26 changes: 26 additions & 0 deletions gnovm/tests/files/type38.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import "fmt"

type IntArray []int
type Arr *IntArray

func add(arr Arr) { // receiver is val, not ptr
*arr = append(*arr, 1)
}

func main() {
a := new(IntArray)
add(a)

fmt.Println(a)
fmt.Println(*a)
fmt.Println(len(*a))
fmt.Println((*a)[0])
}

// Output:
// &[1]
// [1]
// 1
// 1
22 changes: 22 additions & 0 deletions gnovm/tests/files/type39.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

type foo interface {
say()
}

func (f foo) echo() int {
return 1
}

type Bar struct{}

func (b *Bar) say() {}

func main() {
var f foo
f = &Bar{}
println(f.echo())
}

// Error:
// main/files/type39.gno:7: invalid receiver type main.foo (base type is interface type)
24 changes: 24 additions & 0 deletions gnovm/tests/files/type39a.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

type foo interface {
say()
}

type FF foo

func (f FF) echo() int {
return 1
}

type Bar struct{}

func (b *Bar) say() {}

func main() {
var f foo
f = &Bar{}
println(f.echo())
}

// Error:
// main/files/type39a.gno:9: invalid receiver type main.FF (base type is interface type)
22 changes: 22 additions & 0 deletions gnovm/tests/files/type39b.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

type foo interface {
say()
}

func (f *foo) echo() int {
return 1
}

type Bar struct{}

func (b *Bar) say() {}

func main() {
var f *foo
*f = &Bar{}
println(f.echo())
}

// Error:
// main/files/type39b.gno:7: invalid receiver type *main.foo (base type is interface type)

0 comments on commit ca6566f

Please sign in to comment.