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 7 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 @@ func (ss Body) GetBody() Body {
return ss
}

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

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 @@ func (x *PackageNode) PrepareNewValues(pv *PackageValue) []TypedValue {
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 @@ type BlockNode interface {
Define(Name, TypedValue)
Define2(bool, Name, Type, TypedValue)
GetBody() Body
SetBody(Body)
}

// ----------------------------------------
Expand Down Expand Up @@ -1874,18 +1886,34 @@ func (x *IfStmt) GetBody() Body {
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)")
}

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)")
}

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)")
}

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

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

// ----------------------------------------
// 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 @@ -1872,7 +1872,93 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
} 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 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
}
}
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))
}
// step2:
// .tmp1, .tmp2 := x()
dsx := &AssignStmt{
Lhs: tmpExprs,
Op: DEFINE,
Rhs: n.Rhs,
}
dsx.SetLine(n.Line)
dsx = Preprocess(store, last, dsx).(*AssignStmt)

// 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),
}
asx.SetLine(n.Line)
asx = Preprocess(store, last, asx).(*AssignStmt)

// step4:
// replace the original stmt with two new stmts
body := last.GetBody()
// 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

// 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)
} // 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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
} 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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
} 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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
}
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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
} 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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
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++ {
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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
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 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
} 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 @@ func (fv *FuncValue) GetBodyFromSource(store Store) []Stmt {
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))
}
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