all: merge master (43c41b5) into gopls-release-branch.0.14
For golang/go#63220
Merge List:
+ 2023-10-16 43c41b5e5 internal/refactor/inline: reify implicit return conversions
Change-Id: Ie2beb052f316efb38d52f7a35557f7c580c19da7
diff --git a/internal/refactor/inline/inline.go b/internal/refactor/inline/inline.go
index 8a6d777..be16161 100644
--- a/internal/refactor/inline/inline.go
+++ b/internal/refactor/inline/inline.go
@@ -760,6 +760,16 @@
needBindingDecl := !allResultsUnreferenced ||
exists(params, func(i int, p *parameter) bool { return p != nil })
+ // The two strategies below overlap for a tail call of {return exprs}:
+ // The expr-context reduction is nice because it keeps the
+ // caller's return stmt and merely switches its operand,
+ // without introducing a new block, but it doesn't work with
+ // implicit return conversions.
+ //
+ // TODO(adonovan): unify these cases more cleanly, allowing return-
+ // operand replacement and implicit conversions, by adding
+ // conversions around each return operand (if not a spread return).
+
// Special case: call to { return exprs }.
//
// Reduces to:
@@ -776,8 +786,7 @@
// callee's body expression, suitably substituted.
if len(calleeDecl.Body.List) == 1 &&
is[*ast.ReturnStmt](calleeDecl.Body.List[0]) &&
- len(calleeDecl.Body.List[0].(*ast.ReturnStmt).Results) > 0 && // not a bare return
- safeReturn(caller, calleeSymbol, callee) {
+ len(calleeDecl.Body.List[0].(*ast.ReturnStmt).Results) > 0 { // not a bare return
results := calleeDecl.Body.List[0].(*ast.ReturnStmt).Results
context := callContext(caller.path)
@@ -839,11 +848,24 @@
if callee.NumResults == 1 {
logf("strategy: reduce expr-context call to { return expr }")
+ // (includes some simple tail-calls)
+
+ // Make implicit return conversion explicit.
+ if callee.TrivialReturns < callee.TotalReturns {
+ results[0] = convert(calleeDecl.Type.Results.List[0].Type, results[0])
+ }
res.old = caller.Call
res.new = results[0]
- } else {
+ return res, nil
+
+ } else if callee.TrivialReturns == callee.TotalReturns {
logf("strategy: reduce spread-context call to { return expr }")
+ // There is no general way to reify conversions in a spread
+ // return, hence the requirement above.
+ //
+ // TODO(adonovan): allow this reduction when no
+ // conversion is required by the context.
// The call returns multiple results but is
// not a standalone call statement. It must
@@ -880,8 +902,8 @@
default:
return nil, fmt.Errorf("internal error: unexpected context %T for spread call", context)
}
+ return res, nil
}
- return res, nil
}
}
@@ -911,7 +933,7 @@
// or implicit) return.
if ret, ok := callContext(caller.path).(*ast.ReturnStmt); ok &&
len(ret.Results) == 1 &&
- safeReturn(caller, calleeSymbol, callee) &&
+ tailCallSafeReturn(caller, calleeSymbol, callee) &&
!callee.HasBareReturn &&
(!needBindingDecl || bindingDeclStmt != nil) &&
!hasLabelConflict(caller.path, callee.Labels) &&
@@ -2624,9 +2646,9 @@
return names
}
-// safeReturn reports whether the callee's return statements may be safely
+// tailCallSafeReturn reports whether the callee's return statements may be safely
// used to return from the function enclosing the caller (which must exist).
-func safeReturn(caller *Caller, calleeSymbol *types.Func, callee *gobCallee) bool {
+func tailCallSafeReturn(caller *Caller, calleeSymbol *types.Func, callee *gobCallee) bool {
// It is safe if all callee returns involve only trivial conversions.
if callee.TrivialReturns == callee.TotalReturns {
return true
diff --git a/internal/refactor/inline/inline_test.go b/internal/refactor/inline/inline_test.go
index 99cc0de..525be74 100644
--- a/internal/refactor/inline/inline_test.go
+++ b/internal/refactor/inline/inline_test.go
@@ -399,6 +399,16 @@
var _ = 3
}`,
},
+ {
+ // (a regression test for a missing conversion)
+ "Implicit return conversions are inserted in expr-context reduction.",
+ `func f(x int) error { return nil }`,
+ `func _() { if err := f(0); err != nil {} }`,
+ `func _() {
+ if err := error(nil); err != nil {
+ }
+}`,
+ },
})
}
@@ -673,7 +683,7 @@
"Tail call with non-trivial return conversion (caller.sig != callee.sig).",
`func f() error { return E{} }; type E struct{error}`,
`func _() any { return f() }`,
- `func _() any { return func() error { return E{} }() }`,
+ `func _() any { return error(E{}) }`,
},
})
}
@@ -714,6 +724,12 @@
`func _() (int, error) { return f() }`,
`func _() (int, error) { return 0, nil }`,
},
+ {
+ "Implicit return conversions defeat reduction of spread returns, for now.",
+ `func f(x int) (_, _ error) { return nil, nil }`,
+ `func _() { _, _ = f(0) }`,
+ `func _() { _, _ = func() (_, _ error) { return nil, nil }() }`,
+ },
})
}