Skip to content
Merged
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
3 changes: 1 addition & 2 deletions type_var_and_const.go
Original file line number Diff line number Diff line change
Expand Up @@ -734,8 +734,7 @@ func (p *VarDefs) NewAndInit(fn F, pos token.Pos, typ types.Type, names ...strin
decl := p.pkg.newValueDecl(p.NewPos(), p.scope, pos, token.VAR, typ, names...)
if fn != nil {
cb := decl.InitStart(p.pkg)
n := fn(cb)
cb.EndInit(n)
callInitExpr(cb, fn)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While using callInitExpr is good for consistency with ClassDefs.NewAndInit, the current implementation of callInitExpr appears to have a potential bug related to double cleanup.

If fn returns 0 and EndInit(0) doesn't panic (e.g., when NewAndInit is called with no variable names), callInitExpr will execute EndInit(0), and then its deferred function will call ResetInit(). This results in cleanup logic (like commitStmt) being invoked twice, which can lead to incorrect statement ordering.

To fix this, callInitExpr should be refactored to ensure that either EndInit or ResetInit is called, but never both in a non-panicking execution path. Here's a suggested implementation that addresses this while maintaining panic safety:

func callInitExpr(cb *CodeBuilder, fn F) {
	panicking := true
	defer func() {
		if panicking {
			cb.ResetInit()
		}
	}()
	n := fn(cb)
	if n == 0 {
		cb.ResetInit()
	} else {
		cb.EndInit(n)
	}
	panicking = false
}

This revised logic correctly handles all cases, including panics from fn or EndInit, and avoids the double-cleanup problem.

}
}

Expand Down
Loading