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 6 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
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 @@ -1376,6 +1380,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 @@ -1481,6 +1492,7 @@
Define(Name, TypedValue)
Define2(bool, Name, Type, TypedValue)
GetBody() Body
SetBody(Body)
}

// ----------------------------------------
Expand Down Expand Up @@ -1870,18 +1882,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 1886 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1885-L1886

Added lines #L1885 - L1886 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 1894 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1893-L1894

Added lines #L1893 - L1894 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 1902 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1901-L1902

Added lines #L1901 - L1902 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 1910 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1909-L1910

Added lines #L1909 - L1910 were not covered by tests
}

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

Expand Down
88 changes: 87 additions & 1 deletion gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,93 @@
} 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)
if len(n.Lhs) != len(cft.Results) {
piux2 marked this conversation as resolved.
Show resolved Hide resolved
panic(fmt.Sprintf(
"assignment mismatch: "+
"%d variables but %s returns %d values",
len(n.Lhs), cx.Func.String(), len(cft.Results)))

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1677-L1680

Added lines #L1677 - L1680 were not covered by tests
}

// 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 1690 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1689-L1690

Added lines #L1689 - L1690 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()
// .tmp1, .tmp2 := x() assignment statement expression (Op=DEFINE)
// a,b = .tmp1, .tmp2 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 an decompostion
piux2 marked this conversation as resolved.
Show resolved Hide resolved
// because there could be multiple decomposition happens
// we use both stmt index and resturn result number to differentiate the .tmp variables created in each assignment decompostion
piux2 marked this conversation as resolved.
Show resolved Hide resolved
// ex. .tmp_3_2: this variable is created as the 3rd statement in the block, the 2nd parameter returned from x(),
// create .tmp_1_1, .tmp_1_2 .... based on number of result from x()
var tmpExprs Exprs
piux2 marked this conversation as resolved.
Show resolved Hide resolved
for i := range cft.Results {
rn := fmt.Sprintf(".tmp_%d_%d", index, i)
tmpExprs = append(tmpExprs, Nx(rn))

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1706-L1709

Added lines #L1706 - L1709 were not covered by tests
}
// step2:
// .tmp1, .tmp2 := x()
dsx := &AssignStmt{
Lhs: tmpExprs,
Op: DEFINE,
Rhs: n.Rhs,

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1713-L1716

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

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1718-L1719

Added lines #L1718 - L1719 were not covered by tests

// step3:

// a,b = .tmp1, .tmp2
// assign stmt expression
// the right hand side will be converted to call expr for nameed/unnamed covnersion
// we make a copy of tmpExprs to prevent dsx.Lhs in the preview statement changing by the side effect
piux2 marked this conversation as resolved.
Show resolved Hide resolved
// when asx.Rhs is converted to const call expr during the preprocess of the next statement

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

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1729-L1732

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

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1734-L1735

Added lines #L1734 - L1735 were not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1739

Added line #L1739 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 1744 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1741-L1744

Added lines #L1741 - L1744 were not covered by tests

// insert a assignment statement a,b = .tmp1,.tmp2 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 1748 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L1747-L1748

Added lines #L1747 - L1748 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
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 %s",
piux2 marked this conversation as resolved.
Show resolved Hide resolved
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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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
// BlockStmt
{
u1, n2 = x()
// .tmp0_1, .tmp0_2 := x()
// u1, n2 = .tmp0_1, .tmp0_2
println(u1)
println(n2)
println(u1)
println(n2)
}
}

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