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

feat: named and unnamed type assignment 3 of 3 #2367

Merged
merged 19 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion gnovm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ lint:
$(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint run --config ../.github/golangci.yml ./...

.PHONY: fmt
fmt:
fmt:
go run ./cmd/gno fmt $(GNOFMT_FLAGS) ./stdlibs/...
$(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) .

Expand Down
28 changes: 28 additions & 0 deletions gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,10 @@
return ss
}

func (ss *Body) SetBody(nb Body) {
*ss = nb

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L675-L676

Added lines #L675 - L676 were not covered by tests
}

func (ss Body) GetLabeledStmt(label Name) (stmt Stmt, idx int) {
for idx, stmt = range ss {
if label == stmt.GetLabel() {
Expand Down Expand Up @@ -1375,6 +1379,13 @@
panic("PackageNode.PrepareNewValues() package mismatch")
}
}
// The FuncValue Body may have been altered during the preprocessing.
// We need to update body field from the source in the FuncValue accordingly.
for _, tv := range x.Values {
if fv, ok := tv.V.(*FuncValue); ok {
fv.UpdateBodyFromSource()
deelawn marked this conversation as resolved.
Show resolved Hide resolved
piux2 marked this conversation as resolved.
Show resolved Hide resolved
}
}
pvl := len(block.Values)
pnl := len(x.Values)
// copy new top-level defined values/types.
Expand Down Expand Up @@ -1480,6 +1491,7 @@
Define(Name, TypedValue)
Define2(bool, Name, Type, TypedValue)
GetBody() Body
SetBody(Body)
}

// ----------------------------------------
Expand Down Expand Up @@ -1873,18 +1885,34 @@
panic("IfStmt has no body (but .Then and .Else do)")
}

func (x *IfStmt) SetBody(b Body) {
panic("IfStmt has no body (but .Then and .Else do)")

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1888-L1889

Added lines #L1888 - L1889 were not covered by tests
}

func (x *SwitchStmt) GetBody() Body {
panic("SwitchStmt has no body (but its cases do)")
}

func (x *SwitchStmt) SetBody(b Body) {
panic("SwitchStmt has no body (but its cases do)")

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1896-L1897

Added lines #L1896 - L1897 were not covered by tests
}

func (x *FileNode) GetBody() Body {
panic("FileNode has no body (but it does have .Decls)")
}

func (x *FileNode) SetBody(b Body) {
panic("FileNode has no body (but it does have .Decls)")

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1904-L1905

Added lines #L1904 - L1905 were not covered by tests
}

func (x *PackageNode) GetBody() Body {
panic("PackageNode has no body")
}

func (x *PackageNode) SetBody(b Body) {
panic("PackageNode has no body")

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1912-L1913

Added lines #L1912 - L1913 were not covered by tests
}

// ----------------------------------------
// Value Path

Expand Down
105 changes: 103 additions & 2 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"fmt"
"math/big"
"reflect"
"strings"
"sync/atomic"

"github.com/gnolang/gno/tm2/pkg/errors"
Expand Down Expand Up @@ -426,6 +427,24 @@
switch n := n.(type) {
// TRANS_ENTER -----------------------
case *AssignStmt:
if n.Op == DEFINE {
for _, lx := range n.Lhs {
ln := lx.(*NameExpr).Name
if ln == blankIdentifier {
// ignore.
} else if strings.HasPrefix(string(ln), ".decompose_") {
_, ok := last.GetLocalIndex(ln)
if !ok {

Check warning on line 437 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L436-L437

Added lines #L436 - L437 were not covered by tests
// initial declaration to be re-defined.
last.Predefine(false, ln)
} else {

Check warning on line 440 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L439-L440

Added lines #L439 - L440 were not covered by tests
// do not redeclare.
}
}
}
} else {
// nothing defined.
}

// TRANS_ENTER -----------------------
case *ImportDecl, *ValueDecl, *TypeDecl, *FuncDecl:
Expand Down Expand Up @@ -1872,7 +1891,88 @@
} else { // ASSIGN, or assignment operation (+=, -=, <<=, etc.)
// NOTE: Keep in sync with DEFINE above.
if len(n.Lhs) > len(n.Rhs) {
// check is done in assertCompatible
// check is done in assertCompatible where we also
// asserted we have at lease one element in Rhs
if cx, ok := n.Rhs[0].(*CallExpr); ok {
// we decompose the a,b = x(...) for named and unamed
// type value return in an assignments
// Call case: a, b = x(...)
ift := evalStaticTypeOf(store, last, cx.Func)
cft := getGnoFuncTypeOf(store, ift)
// check if we we need to decompose for named typed conversion in the function return results
var decompose bool

for i, rhsType := range cft.Results {
lt := evalStaticTypeOf(store, last, n.Lhs[i])
if lt != nil && isNamedConversion(rhsType.Type, lt) {
decompose = true
break

Check warning on line 1909 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1908-L1909

Added lines #L1908 - L1909 were not covered by tests
}
}
if decompose {
// only enter this section if cft.Results to be converted to Lhs type for named type conversion.
// decompose a,b = x()
// .decompose1, .decompose2 := x() assignment statement expression (Op=DEFINE)
// a,b = .decompose1, .decompose2 assignment statement expression ( Op=ASSIGN )
// add the new statement to last.Body

// step1:
// create a hidden var with leading . (dot) the curBodyLen increase every time when there is a decomposition
// because there could be multiple decomposition happens
// we use both stmt index and return result number to differentiate the .decompose variables created in each assignment decompostion
// ex. .decompose_3_2: this variable is created as the 3rd statement in the block, the 2nd parameter returned from x(),
// create .decompose_1_1, .decompose_1_2 .... based on number of result from x()
tmpExprs := make(Exprs, 0, len(cft.Results))
for i := range cft.Results {
rn := fmt.Sprintf(".decompose_%d_%d", index, i)
tmpExprs = append(tmpExprs, Nx(rn))

Check warning on line 1928 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1925-L1928

Added lines #L1925 - L1928 were not covered by tests
}
// step2:
// .decompose1, .decompose2 := x()
dsx := &AssignStmt{
Lhs: tmpExprs,
Op: DEFINE,
Rhs: n.Rhs,

Check warning on line 1935 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1932-L1935

Added lines #L1932 - L1935 were not covered by tests
}
dsx.SetLine(n.Line)
dsx = Preprocess(store, last, dsx).(*AssignStmt)

Check warning on line 1938 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1937-L1938

Added lines #L1937 - L1938 were not covered by tests

// step3:

// a,b = .decompose1, .decompose2
// assign stmt expression
// The right-hand side will be converted to a call expression for named/unnamed conversion.
// tmpExprs is a []Expr; we make a copy of tmpExprs to prevent dsx.Lhs in the previous statement (dsx) from being changed by side effects.
// If we don't copy tmpExprs, when asx.Rhs is converted to a const call expression during the preprocessing of the AssignStmt asx,
// dsx.Lhs will change from []NameExpr to []CallExpr.
// This side effect would cause a panic when the machine executes the dsx statement, as it expects Lhs to be []NameExpr.

asx := &AssignStmt{
Lhs: n.Lhs,
Op: ASSIGN,
Rhs: copyExprs(tmpExprs),

Check warning on line 1953 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1950-L1953

Added lines #L1950 - L1953 were not covered by tests
}
asx.SetLine(n.Line)
asx = Preprocess(store, last, asx).(*AssignStmt)

Check warning on line 1956 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1955-L1956

Added lines #L1955 - L1956 were not covered by tests

// step4:
// replace the original stmt with two new stmts
body := last.GetBody()

Check warning on line 1960 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1960

Added line #L1960 was not covered by tests
// we need to do an in-place replacement while leaving the current node
n.Attributes = dsx.Attributes
n.Lhs = dsx.Lhs
n.Op = dsx.Op
n.Rhs = dsx.Rhs

Check warning on line 1965 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1962-L1965

Added lines #L1962 - L1965 were not covered by tests

// insert a assignment statement a,b = .decompose1,.decompose2 AFTER the current statement in the last.Body.
body = append(body[:index+1], append(Body{asx}, body[index+1:]...)...)
last.SetBody(body)

Check warning on line 1969 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1968-L1969

Added lines #L1968 - L1969 were not covered by tests
} // end of the decomposition

// Last step: we need to insert the statements to FuncValue.body of PackageNopde.Values[i].V
// updating FuncValue.body=FuncValue.Source.Body in updates := pn.PrepareNewValues(pv) during preprocess.
// we updated FuncValue from source.
}
} else { // len(Lhs) == len(Rhs)
if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN {
if len(n.Lhs) != 1 || len(n.Rhs) != 1 {
Expand Down Expand Up @@ -3160,7 +3260,7 @@
} else {
panic("should not happen")
}

// The body may get altered during preprocessing later.
if !dt.TryDefineMethod(&FuncValue{
Type: ft,
IsMethod: true,
Expand Down Expand Up @@ -3388,6 +3488,7 @@
pkg := skipFile(last).(*PackageNode)
// define a FuncValue w/ above type as d.Name.
// fill in later during *FuncDecl:BLOCK.
// The body may get altered during preprocessing later.
fv := &FuncValue{
Type: ft,
IsMethod: false,
Expand Down
27 changes: 18 additions & 9 deletions gnovm/pkg/gnolang/transcribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@
} else {
cnn = cnn2.(*FuncLitExpr)
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand Down Expand Up @@ -383,7 +384,8 @@
} else {
cnn = cnn2.(*BlockStmt)
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_BLOCK_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand All @@ -393,7 +395,8 @@
}
case *BranchStmt:
case *DeclStmt:
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_DECL_BODY, idx, cnn.Body[idx], &c).(SimpleDeclStmt)
if isBreak(c) {
break
Expand Down Expand Up @@ -438,7 +441,8 @@
return
}
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_FOR_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand Down Expand Up @@ -488,7 +492,8 @@
} else {
cnn = cnn2.(*IfCaseStmt)
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_IF_CASE_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand Down Expand Up @@ -525,7 +530,8 @@
return
}
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_RANGE_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand Down Expand Up @@ -565,7 +571,8 @@
if isStopOrSkip(nc, c) {
return
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {

Check warning on line 575 in gnovm/pkg/gnolang/transcribe.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/transcribe.go#L575

Added line #L575 was not covered by tests
cnn.Body[idx] = transcribe(t, nns, TRANS_SELECTCASE_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand Down Expand Up @@ -640,7 +647,8 @@
return
}
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_SWITCHCASE_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand All @@ -666,7 +674,8 @@
} else {
cnn = cnn2.(*FuncDecl)
}
for idx := range cnn.Body {
// iterate over Body; its length can change if a statement is decomposed.
for idx := 0; idx < len(cnn.Body); idx++ {
cnn.Body[idx] = transcribe(t, nns, TRANS_FUNC_BODY, idx, cnn.Body[idx], &c).(Stmt)
if isBreak(c) {
break
Expand Down
9 changes: 9 additions & 0 deletions gnovm/pkg/gnolang/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,15 @@
return fv.body
}

func (fv *FuncValue) UpdateBodyFromSource() {
if fv.Source == nil {
panic(fmt.Sprintf(
"Source is missing for FuncValue %q",
fv.Name))

Check warning on line 608 in gnovm/pkg/gnolang/values.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/values.go#L606-L608

Added lines #L606 - L608 were not covered by tests
}
fv.body = fv.Source.GetBody()
}

func (fv *FuncValue) GetSource(store Store) BlockNode {
if rn, ok := fv.Source.(RefNode); ok {
source := store.GetBlockNode(rn.GetLocation())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

type nat []int

func x() (nat, []int) {
a := nat{1}
b := []int{2}
return a, b
}

func main() {
var u1 []int
var n2 nat

_, n2 = x()
// .tmp1, .tmp_2 := x()
// _, u2 = .tmp1, .tmp_2

println(u1)
println(n2)

}

// Output:
// (nil []int)
// (slice[(2 int)] main.nat)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

type nat []int

func x() (nat, []int) {
a := nat{1}
b := []int{2}
return a, b
}

func main() {
var u1 []int
var n2 nat

u1, _ = x()
// .tmp1, .tmp_2 := x()
// u1, _ = .tmp1, .tmp_2

println(u1)
println(n2)

}

// Output:
// slice[(1 int)]
// (nil main.nat)
Loading
Loading