Skip to content

Commit a27a525

Browse files
adonovangopherbot
authored andcommitted
go/types: set correct Var.scopePos for parameters/results
Previously, its value was unset (NoPos), but the correct value is a point after the signature (FuncType.End) and before the body. Also, fix a bug in Scope.Innermost whereby it would return the wrong (outer) scope when the query position was in the FuncType portion of a Func{Decl,Lit}. The fix is to set the scope's pos/end to those of the complete Func{Decl,Lit}. This is now documented at Info.Scopes, along with other missing information. Also, fix a bug in the go/types (but not types2) scope test, in which comments were discarded by the parser, causing the entire test to be a no-op (!). Also, make failures of TestScopeLookupParent more informative. Also, add a release note about the change in behavior. Fixes #64292 Fixes #64295 Change-Id: Ib681f59d1b0b43de977666db08302d7524d3305f Reviewed-on: https://go-review.googlesource.com/c/go/+/544035 Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Auto-Submit: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Run-TryBot: Robert Griesemer <[email protected]>
1 parent 858cd8d commit a27a525

File tree

16 files changed

+169
-47
lines changed

16 files changed

+169
-47
lines changed

doc/go1.22.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,18 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
486486
</dd>
487487
</dl><!-- os/exec -->
488488

489+
<dl id="go/types"><dt><a href="/pkg/go/types/">go/types</a></dt>
490+
<dd>
491+
<p><!-- https://go.dev/issue/64295, CL 544035 -->
492+
The start position (<a href="/pkg/go/types#Scope.Pos">Pos</a>)
493+
of the lexical environment block (<a href="/pkg/go/types#Scope">Scope</a>)
494+
that represents a function body has changed:
495+
it used to start at the opening curly brace of the function body,
496+
but now starts at the function's <code>func</code> token.
497+
</p>
498+
</dd>
499+
</dl>
500+
489501
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
490502
<dd>
491503
<p><!-- https://go.dev/issue/61827, CL 517777 -->

src/cmd/compile/internal/types2/api.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ type Info struct {
268268
// scope, the function scopes are embedded in the file scope of the file
269269
// containing the function declaration.
270270
//
271+
// The Scope of a function contains the declarations of any
272+
// type parameters, parameters, and named results, plus any
273+
// local declarations in the body block.
274+
// It is coextensive with the complete extent of the
275+
// function's syntax ([*ast.FuncDecl] or [*ast.FuncLit]).
276+
// The Scopes mapping does not contain an entry for the
277+
// function body ([*ast.BlockStmt]); the function's scope is
278+
// associated with the [*ast.FuncType].
279+
//
271280
// The following node types may appear in Scopes:
272281
//
273282
// *syntax.File

src/cmd/compile/internal/types2/api_test.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,12 +1892,12 @@ const Pi = 3.1415
18921892
type T struct{}
18931893
var Y, _ = lib.X, X
18941894
1895-
func F(){
1895+
func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ {
18961896
const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
18971897
type /*t=undef*/ t /*t=typename:14*/ *t
18981898
print(Y) /*Y=var:10*/
18991899
x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
1900-
var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
1900+
var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
19011901
19021902
var a []int
19031903
for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
@@ -1916,6 +1916,10 @@ func F(){
19161916
println(int)
19171917
default /*int=var:31*/ :
19181918
}
1919+
1920+
_ = param1
1921+
_ = res1
1922+
return
19191923
}
19201924
/*main=undef*/
19211925
`
@@ -1981,7 +1985,29 @@ func F(){
19811985

19821986
_, gotObj := inner.LookupParent(id.Value, id.Pos())
19831987
if gotObj != wantObj {
1984-
t.Errorf("%s: got %v, want %v", id.Pos(), gotObj, wantObj)
1988+
// Print the scope tree of mainScope in case of error.
1989+
var printScopeTree func(indent string, s *Scope)
1990+
printScopeTree = func(indent string, s *Scope) {
1991+
t.Logf("%sscope %s %v-%v = %v",
1992+
indent,
1993+
ScopeComment(s),
1994+
s.Pos(),
1995+
s.End(),
1996+
s.Names())
1997+
for i := range s.NumChildren() {
1998+
printScopeTree(indent+" ", s.Child(i))
1999+
}
2000+
}
2001+
printScopeTree("", mainScope)
2002+
2003+
t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
2004+
id.Pos(),
2005+
ScopeComment(inner),
2006+
id.Value,
2007+
id.Pos(),
2008+
gotObj,
2009+
wantObj,
2010+
ObjectScopePos(wantObj))
19852011
continue
19862012
}
19872013
}

src/cmd/compile/internal/types2/decl.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,11 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel
570570
// Declare type parameters up-front.
571571
// The scope of type parameters starts at the beginning of the type parameter
572572
// list (so we can have mutually recursive parameterized type bounds).
573-
for i, f := range list {
574-
tparams[i] = check.declareTypeParam(f.Name)
573+
if len(list) > 0 {
574+
scopePos := list[0].Pos()
575+
for i, f := range list {
576+
tparams[i] = check.declareTypeParam(f.Name, scopePos)
577+
}
575578
}
576579

577580
// Set the type parameters before collecting the type constraints because
@@ -628,16 +631,16 @@ func (check *Checker) bound(x syntax.Expr) Type {
628631
return check.typ(x)
629632
}
630633

631-
func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam {
634+
func (check *Checker) declareTypeParam(name *syntax.Name, scopePos syntax.Pos) *TypeParam {
632635
// Use Typ[Invalid] for the type constraint to ensure that a type
633636
// is present even if the actual constraint has not been assigned
634637
// yet.
635638
// TODO(gri) Need to systematically review all uses of type parameter
636639
// constraints to make sure we don't rely on them if they
637640
// are not properly set yet.
638641
tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
639-
tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect
640-
check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
642+
tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect
643+
check.declare(check.scope, name, tname, scopePos)
641644
return tpar
642645
}
643646

@@ -750,6 +753,11 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
750753
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
751754
obj.color_ = saved
752755

756+
// Set the scope's extent to the complete "func (...) { ... }"
757+
// so that Scope.Innermost works correctly.
758+
sig.scope.pos = fdecl.Pos()
759+
sig.scope.end = syntax.EndPos(fdecl)
760+
753761
if len(fdecl.TParamList) > 0 && fdecl.Body == nil {
754762
check.softErrorf(fdecl, BadDecl, "generic function is missing function body")
755763
}

src/cmd/compile/internal/types2/expr.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,10 @@ func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type)
10811081

10821082
case *syntax.FuncLit:
10831083
if sig, ok := check.typ(e.Type).(*Signature); ok {
1084+
// Set the Scope's extent to the complete "func (...) {...}"
1085+
// so that Scope.Innermost works correctly.
1086+
sig.scope.pos = e.Pos()
1087+
sig.scope.end = syntax.EndPos(e)
10841088
if !check.conf.IgnoreFuncBodies && e.Body != nil {
10851089
// Anonymous functions are considered part of the
10861090
// init expression/func declaration which contains

src/cmd/compile/internal/types2/signature.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,12 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
108108
// - the receiver specification acts as local declaration for its type parameters, which may be blank
109109
_, rname, rparams := check.unpackRecv(recvPar.Type, true)
110110
if len(rparams) > 0 {
111+
// The scope of the type parameter T in "func (r T[T]) f()"
112+
// starts after f, not at "r"; see #52038.
113+
scopePos := ftyp.Pos()
111114
tparams := make([]*TypeParam, len(rparams))
112115
for i, rparam := range rparams {
113-
tparams[i] = check.declareTypeParam(rparam)
116+
tparams[i] = check.declareTypeParam(rparam, scopePos)
114117
}
115118
sig.rparams = bindTParams(tparams)
116119
// Blank identifiers don't get declared, so naive type-checking of the
@@ -167,16 +170,21 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
167170
check.collectTypeParams(&sig.tparams, tparams)
168171
}
169172

170-
// Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
171-
// declarations and then squash that scope into the parent scope (and report any redeclarations at
172-
// that time).
173+
// Use a temporary scope for all parameter declarations and then
174+
// squash that scope into the parent scope (and report any
175+
// redeclarations at that time).
176+
//
177+
// TODO(adonovan): now that each declaration has the correct
178+
// scopePos, there should be no need for scope squashing.
179+
// Audit to ensure all lookups honor scopePos and simplify.
173180
scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
174-
var recvList []*Var // TODO(gri) remove the need for making a list here
181+
scopePos := syntax.EndPos(ftyp) // all parameters' scopes start after the signature
182+
var recvList []*Var // TODO(gri) remove the need for making a list here
175183
if recvPar != nil {
176-
recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false) // use rewritten receiver type, if any
184+
recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false, scopePos) // use rewritten receiver type, if any
177185
}
178-
params, variadic := check.collectParams(scope, ftyp.ParamList, true)
179-
results, _ := check.collectParams(scope, ftyp.ResultList, false)
186+
params, variadic := check.collectParams(scope, ftyp.ParamList, true, scopePos)
187+
results, _ := check.collectParams(scope, ftyp.ResultList, false, scopePos)
180188
scope.Squash(func(obj, alt Object) {
181189
var err error_
182190
err.code = DuplicateDecl
@@ -259,7 +267,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
259267

260268
// collectParams declares the parameters of list in scope and returns the corresponding
261269
// variable list.
262-
func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool) (params []*Var, variadic bool) {
270+
func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool, scopePos syntax.Pos) (params []*Var, variadic bool) {
263271
if list == nil {
264272
return
265273
}
@@ -294,7 +302,7 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadic
294302
// ok to continue
295303
}
296304
par := NewParam(field.Name.Pos(), check.pkg, name, typ)
297-
check.declare(scope, field.Name, par, scope.pos)
305+
check.declare(scope, field.Name, par, scopePos)
298306
params = append(params, par)
299307
named = true
300308
} else {

src/cmd/compile/internal/types2/stmt.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
2323
check.trace(body.Pos(), "-- %s: %s", name, sig)
2424
}
2525

26-
// set function scope extent
27-
sig.scope.pos = body.Pos()
28-
sig.scope.end = syntax.EndPos(body)
29-
3026
// save/restore current environment and set up function environment
3127
// (and use 0 indentation at function start)
3228
defer func(env environment, indent int) {

src/cmd/compile/internal/types2/util_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77

88
package types2
99

10-
import "cmd/compile/internal/syntax"
10+
import (
11+
"cmd/compile/internal/syntax"
12+
)
1113

1214
func CmpPos(p, q syntax.Pos) int { return cmpPos(p, q) }
15+
16+
func ScopeComment(s *Scope) string { return s.comment }
17+
func ObjectScopePos(obj Object) syntax.Pos { return obj.scopePos() }

src/go/types/api.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,15 @@ type Info struct {
263263
// scope, the function scopes are embedded in the file scope of the file
264264
// containing the function declaration.
265265
//
266+
// The Scope of a function contains the declarations of any
267+
// type parameters, parameters, and named results, plus any
268+
// local declarations in the body block.
269+
// It is coextensive with the complete extent of the
270+
// function's syntax ([*ast.FuncDecl] or [*ast.FuncLit]).
271+
// The Scopes mapping does not contain an entry for the
272+
// function body ([*ast.BlockStmt]); the function's scope is
273+
// associated with the [*ast.FuncType].
274+
//
266275
// The following node types may appear in Scopes:
267276
//
268277
// *ast.File

src/go/types/api_test.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
var nopos token.Pos
2828

2929
func mustParse(fset *token.FileSet, src string) *ast.File {
30-
f, err := parser.ParseFile(fset, pkgName(src), src, 0)
30+
f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments)
3131
if err != nil {
3232
panic(err) // so we don't need to pass *testing.T
3333
}
@@ -1896,12 +1896,12 @@ const Pi = 3.1415
18961896
type T struct{}
18971897
var Y, _ = lib.X, X
18981898
1899-
func F(){
1899+
func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ {
19001900
const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
19011901
type /*t=undef*/ t /*t=typename:14*/ *t
19021902
print(Y) /*Y=var:10*/
19031903
x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
1904-
var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
1904+
var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
19051905
19061906
var a []int
19071907
for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
@@ -1920,6 +1920,10 @@ func F(){
19201920
println(int)
19211921
default /*int=var:31*/ :
19221922
}
1923+
1924+
_ = param1
1925+
_ = res1
1926+
return
19231927
}
19241928
/*main=undef*/
19251929
`
@@ -1981,8 +1985,29 @@ func F(){
19811985

19821986
_, gotObj := inner.LookupParent(id.Name, id.Pos())
19831987
if gotObj != wantObj {
1984-
t.Errorf("%s: got %v, want %v",
1985-
fset.Position(id.Pos()), gotObj, wantObj)
1988+
// Print the scope tree of mainScope in case of error.
1989+
var printScopeTree func(indent string, s *Scope)
1990+
printScopeTree = func(indent string, s *Scope) {
1991+
t.Logf("%sscope %s %v-%v = %v",
1992+
indent,
1993+
ScopeComment(s),
1994+
s.Pos(),
1995+
s.End(),
1996+
s.Names())
1997+
for i := range s.NumChildren() {
1998+
printScopeTree(indent+" ", s.Child(i))
1999+
}
2000+
}
2001+
printScopeTree("", mainScope)
2002+
2003+
t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
2004+
fset.Position(id.Pos()),
2005+
ScopeComment(inner),
2006+
id.Name,
2007+
id.Pos(),
2008+
gotObj,
2009+
wantObj,
2010+
ObjectScopePos(wantObj))
19862011
continue
19872012
}
19882013
}

0 commit comments

Comments
 (0)