cmd/compile, go/types: restore 'too many return values' error for func with no results

Currently the code handles the case of returning values from
a function with no result parameters as a special case.
Consider this input:

	package p

	func f0_2()            { return 1, 2 }
	func f0_1()            { return 1 }
	func f1_0() int        { return }
	func f1_2() int        { return 1, 2 }
	func f2_0() (int, int) { return }
	func f2_1() (int, int) { return 1 }

The errors are:

	x.go:3:33: no result values expected   <<<
	x.go:4:33: no result values expected   <<<
	x.go:5:26: not enough return values
		have ()
		want (int)
	x.go:6:36: too many return values
		have (number, number)
		want (int)
	x.go:7:26: not enough return values
		have ()
		want (int, int)
	x.go:8:33: not enough return values
		have (number)
		want (int, int)

There are two problems with the current special case emitting the
errors on the marked line:

1. It calls them 'result values' instead of 'return values'.
2. It doesn't show the type being returned, which can be useful to programmers.

Using the general case solves both these problems,
so this CL removes the special case and calls the general case instead.

Now those two errors read:

	x.go:3:33: too many return values
		have (number, number)
		want ()
	x.go:4:33: too many return values
		have (number)
		want ()

Fixes #50653.

Change-Id: If6b47dcece14ed4febb3a2d3d78270d5be1cb24d
Reviewed-on: https://go-review.googlesource.com/c/go/+/379116
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Russ Cox 2022-01-18 10:31:59 -05:00
parent 71888fe4b0
commit cf5d73e8a2
8 changed files with 44 additions and 48 deletions

View File

@ -474,30 +474,28 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
case *syntax.ReturnStmt: case *syntax.ReturnStmt:
res := check.sig.results res := check.sig.results
// Return with implicit results allowed for function with named results.
// (If one is named, all are named.)
results := unpackExpr(s.Results) results := unpackExpr(s.Results)
if res.Len() > 0 { if len(results) == 0 && res.Len() > 0 && res.vars[0].name != "" {
// function returns results // spec: "Implementation restriction: A compiler may disallow an empty expression
// (if one, say the first, result parameter is named, all of them are named) // list in a "return" statement if a different entity (constant, type, or variable)
if len(results) == 0 && res.vars[0].name != "" { // with the same name as a result parameter is in scope at the place of the return."
// spec: "Implementation restriction: A compiler may disallow an empty expression for _, obj := range res.vars {
// list in a "return" statement if a different entity (constant, type, or variable) if alt := check.lookup(obj.name); alt != nil && alt != obj {
// with the same name as a result parameter is in scope at the place of the return." var err error_
for _, obj := range res.vars { err.errorf(s, "result parameter %s not in scope at return", obj.name)
if alt := check.lookup(obj.name); alt != nil && alt != obj { err.errorf(alt, "inner declaration of %s", obj)
var err error_ check.report(&err)
err.errorf(s, "result parameter %s not in scope at return", obj.name) // ok to continue
err.errorf(alt, "inner declaration of %s", obj)
check.report(&err)
// ok to continue
}
} }
} else {
// return has results or result parameters are unnamed
check.initVars(res.vars, results, s)
} }
} else if len(results) > 0 { } else {
check.error(results[0], "no result values expected") var lhs []*Var
check.use(results...) if res.Len() > 0 {
lhs = res.vars
}
check.initVars(lhs, results, s)
} }
case *syntax.BranchStmt: case *syntax.BranchStmt:

View File

@ -375,7 +375,7 @@ func continues() {
func returns0() { func returns0() {
return return
return 0 /* ERROR no result values expected */ return 0 /* ERROR too many return values */
} }
func returns1(x float64) (int, *float64) { func returns1(x float64) (int, *float64) {

View File

@ -177,8 +177,8 @@ func _() {
func _() { func _() {
var x int var x int
return x /* ERROR no result values expected */ return x /* ERROR too many return values */
return math /* ERROR no result values expected */ .Sin(0) return math /* ERROR too many return values */ .Sin(0)
} }
func _() int { func _() int {

View File

@ -503,27 +503,25 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
case *ast.ReturnStmt: case *ast.ReturnStmt:
res := check.sig.results res := check.sig.results
if res.Len() > 0 { // Return with implicit results allowed for function with named results.
// function returns results // (If one is named, all are named.)
// (if one, say the first, result parameter is named, all of them are named) if len(s.Results) == 0 && res.Len() > 0 && res.vars[0].name != "" {
if len(s.Results) == 0 && res.vars[0].name != "" { // spec: "Implementation restriction: A compiler may disallow an empty expression
// spec: "Implementation restriction: A compiler may disallow an empty expression // list in a "return" statement if a different entity (constant, type, or variable)
// list in a "return" statement if a different entity (constant, type, or variable) // with the same name as a result parameter is in scope at the place of the return."
// with the same name as a result parameter is in scope at the place of the return." for _, obj := range res.vars {
for _, obj := range res.vars { if alt := check.lookup(obj.name); alt != nil && alt != obj {
if alt := check.lookup(obj.name); alt != nil && alt != obj { check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name) check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj) // ok to continue
// ok to continue
}
} }
} else {
// return has results or result parameters are unnamed
check.initVars(res.vars, s.Results, s)
} }
} else if len(s.Results) > 0 { } else {
check.error(s.Results[0], _WrongResultCount, "no result values expected") var lhs []*Var
check.use(s.Results...) if res.Len() > 0 {
lhs = res.vars
}
check.initVars(lhs, s.Results, s)
} }
case *ast.BranchStmt: case *ast.BranchStmt:

View File

@ -375,7 +375,7 @@ func continues() {
func returns0() { func returns0() {
return return
return 0 /* ERROR no result values expected */ return 0 /* ERROR too many return values */
} }
func returns1(x float64) (int, *float64) { func returns1(x float64) (int, *float64) {

View File

@ -169,8 +169,8 @@ func _() {
func _() { func _() {
var x int var x int
return x /* ERROR no result values expected */ return x /* ERROR too many return values */
return math /* ERROR no result values expected */ .Sin(0) return math /* ERROR too many return values */ .Sin(0)
} }
func _() int { func _() int {

View File

@ -11,7 +11,7 @@ func foo() (int, int) {
} }
func foo2() { func foo2() {
return int(2), 2 // ERROR "too many arguments to return\n\thave \(int, number\)\n\twant \(\)|return with value in function with no return type|no result values expected" return int(2), 2 // ERROR "too many (arguments to return|return values)\n\thave \(int, number\)\n\twant \(\)|return with value in function with no return type"
} }
func foo3(v int) (a, b, c, d int) { func foo3(v int) (a, b, c, d int) {

View File

@ -20,5 +20,5 @@ func _() int {
} }
func _() { func _() {
return 1 // ERROR "too many arguments to return\n\thave \(number\)\n\twant \(\)|no result values expected" return 1 // ERROR "too many (arguments to return|return values)\n\thave \(number\)\n\twant \(\)"
} }