Skip to content

Commit

Permalink
Updated defer lambda semantics to match go statement
Browse files Browse the repository at this point in the history
  • Loading branch information
ritchiecarroll committed Jan 4, 2025
1 parent 2d96610 commit 80060f4
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// file may cause incorrect behavior and will be lost
// if the code is regenerated.
//
// Generated on 2025 January 02 11:09:14 UTC
// Generated on 2025 January 04 00:40:46 UTC
// </auto-generated>
//---------------------------------------------------------
namespace go;
Expand Down
2 changes: 1 addition & 1 deletion src/Tests/Behavioral/SelectStatement/SelectStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private static void Main() {
goǃ(_ => sendOnly(mychanlʗ1));
var (result, ok) = ᐸꟷ(mychanl, );
fmt.Println(result, ok);
foreach (var v in range(((IntSlice)(s)).All())) {
foreach (var v in range(((IntSlice)s).All())) {
fmt.Println(v);
}
sieve();
Expand Down
48 changes: 41 additions & 7 deletions src/go2cs2/performVariableAnalysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,47 @@ func (v *Visitor) performVariableAnalysis(funcDecl *ast.FuncDecl, signature *typ
v.lambdaCapture.analysisInLambda = false
v.lambdaCapture.currentLambda = nil

// Check for defer and recover calls
case *ast.DeferStmt:
v.hasDefer = true

// Enter capture analysis context for goroutine
v.lambdaCapture.analysisInLambda = true
v.lambdaCapture.currentLambda = node

// Create capture set for this statement
v.lambdaCapture.stmtCaptures[node] = make(map[*ast.Ident]bool)

// Process function expression
ast.Inspect(node.Call.Fun, func(n ast.Node) bool {
if id, ok := n.(*ast.Ident); ok {
v.processPotentialCapture(id)
// If it was captured, associate it with this statement
if _, exists := v.lambdaCapture.capturedVars[id]; exists {
v.lambdaCapture.stmtCaptures[node][id] = true
}
}
return true
})

// Process arguments
for _, arg := range node.Call.Args {
ast.Inspect(arg, func(n ast.Node) bool {
if id, ok := n.(*ast.Ident); ok {
v.processPotentialCapture(id)
// If it was captured, associate it with this statement
if _, exists := v.lambdaCapture.capturedVars[id]; exists {
v.lambdaCapture.stmtCaptures[node][id] = true
}
}
return true
})
}

// Exit capture analysis context
v.lambdaCapture.analysisInLambda = false
v.lambdaCapture.currentLambda = nil

case *ast.IfStmt:
v.scopeStack = append(v.scopeStack, make(map[string]*types.Var))
tracker := registry.get(IfTracker)
Expand Down Expand Up @@ -935,13 +976,6 @@ func (v *Visitor) performVariableAnalysis(funcDecl *ast.FuncDecl, signature *typ
tracker.exit()
v.scopeStack = v.scopeStack[:len(v.scopeStack)-1]

// Check for defer and recover calls
case *ast.DeferStmt:
v.hasDefer = true

// Visit the function call
visitNode(node.Call)

case *ast.CallExpr:
if fun, ok := node.Fun.(*ast.Ident); ok {
if fun.Name == "recover" {
Expand Down
53 changes: 46 additions & 7 deletions src/go2cs2/visitDeferStmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,58 @@ import (
)

func (v *Visitor) visitDeferStmt(deferStmt *ast.DeferStmt) {
v.targetFile.WriteString(v.newline)
v.writeOutput("defer(")
// Analyze captures specifically for this defer statement
v.enterLambdaConversion(deferStmt)
defer v.exitLambdaConversion()

// TODO: Setup to match GoStmt operations
callExpr := v.convCallExpr(deferStmt.Call, DefaultLambdaContext())
lambdaContext := DefaultLambdaContext()

// C# defer implementation expects an Action delegate
// If we have a function literal, only prepare captures there, not on the DeferStmt
if funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok {
if captures, exists := v.lambdaCapture.stmtCaptures[deferStmt]; exists {
v.lambdaCapture.stmtCaptures[funcLit] = captures

// Delete captures from GoStmt to avoid double processing
delete(v.lambdaCapture.stmtCaptures, deferStmt)
lambdaContext.deferredDecls = &strings.Builder{}

}
} else {
v.prepareStmtCaptures(deferStmt)
}

result := strings.Builder{}
wroteDecls := false

callExpr := strings.TrimSpace(v.convCallExpr(deferStmt.Call, lambdaContext))

if lambdaContext.deferredDecls != nil && lambdaContext.deferredDecls.Len() > 0 {
result.WriteString(lambdaContext.deferredDecls.String())
result.WriteString(v.indent(v.indentLevel))
wroteDecls = true
}

if decls := v.generateCaptureDeclarations(); strings.TrimSpace(decls) != "" {
result.WriteString(decls)
wroteDecls = true
}

if !wroteDecls {
result.WriteString(v.newline)
result.WriteString(v.indent(v.indentLevel))
}

result.WriteString("defer(")

// C# `defer` method implementation expects an Action delegate
if strings.HasSuffix(callExpr, "()") {
callExpr = strings.TrimSuffix(callExpr, "()")
} else {
callExpr = "() => " + callExpr
}

v.targetFile.WriteString(callExpr)
v.targetFile.WriteString(");")
result.WriteString(callExpr)
result.WriteString(");")

v.targetFile.WriteString(result.String())
}
6 changes: 2 additions & 4 deletions src/go2cs2/visitGoStmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,9 @@ func (v *Visitor) visitGoStmt(goStmt *ast.GoStmt) {

// C# `go` method implementation expects an Action or WaitCallback delegate
if strings.HasSuffix(callExpr, "()") {
// Call Action delegate overload
callExpr = strings.TrimSuffix(callExpr, "()")
callExpr = strings.TrimSuffix(callExpr, "()") // Action delegate
} else {
// Call WaitCallback delegate overload
callExpr = "_ => " + callExpr
callExpr = "_ => " + callExpr // WaitCallback delegate
}

result.WriteString(callExpr)
Expand Down

0 comments on commit 80060f4

Please sign in to comment.