</dd>
</dl><!-- os/exec -->
+<dl id="go/types"><dt><a href="/pkg/go/types/">go/types</a></dt>
+ <dd>
+ <p><!-- https://go.dev/issue/64295, CL 544035 -->
+ The start position (<a href="/pkg/go/types#Scope.Pos">Pos</a>)
+ of the lexical environment block (<a href="/pkg/go/types#Scope">Scope</a>)
+ that represents a function body has changed:
+ it used to start at the opening curly brace of the function body,
+ but now starts at the function's <code>func</code> token.
+ </p>
+ </dd>
+</dl>
+
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
<dd>
<p><!-- https://go.dev/issue/61827, CL 517777 -->
// scope, the function scopes are embedded in the file scope of the file
// containing the function declaration.
//
+ // The Scope of a function contains the declarations of any
+ // type parameters, parameters, and named results, plus any
+ // local declarations in the body block.
+ // It is coextensive with the complete extent of the
+ // function's syntax ([*ast.FuncDecl] or [*ast.FuncLit]).
+ // The Scopes mapping does not contain an entry for the
+ // function body ([*ast.BlockStmt]); the function's scope is
+ // associated with the [*ast.FuncType].
+ //
// The following node types may appear in Scopes:
//
// *syntax.File
type T struct{}
var Y, _ = lib.X, X
-func F(){
+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*/ {
const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
type /*t=undef*/ t /*t=typename:14*/ *t
print(Y) /*Y=var:10*/
x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
- var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
+ var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
var a []int
for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
println(int)
default /*int=var:31*/ :
}
+
+ _ = param1
+ _ = res1
+ return
}
/*main=undef*/
`
_, gotObj := inner.LookupParent(id.Value, id.Pos())
if gotObj != wantObj {
- t.Errorf("%s: got %v, want %v", id.Pos(), gotObj, wantObj)
+ // Print the scope tree of mainScope in case of error.
+ var printScopeTree func(indent string, s *Scope)
+ printScopeTree = func(indent string, s *Scope) {
+ t.Logf("%sscope %s %v-%v = %v",
+ indent,
+ ScopeComment(s),
+ s.Pos(),
+ s.End(),
+ s.Names())
+ for i := range s.NumChildren() {
+ printScopeTree(indent+" ", s.Child(i))
+ }
+ }
+ printScopeTree("", mainScope)
+
+ t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
+ id.Pos(),
+ ScopeComment(inner),
+ id.Value,
+ id.Pos(),
+ gotObj,
+ wantObj,
+ ObjectScopePos(wantObj))
continue
}
}
// Declare type parameters up-front.
// The scope of type parameters starts at the beginning of the type parameter
// list (so we can have mutually recursive parameterized type bounds).
- for i, f := range list {
- tparams[i] = check.declareTypeParam(f.Name)
+ if len(list) > 0 {
+ scopePos := list[0].Pos()
+ for i, f := range list {
+ tparams[i] = check.declareTypeParam(f.Name, scopePos)
+ }
}
// Set the type parameters before collecting the type constraints because
return check.typ(x)
}
-func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam {
+func (check *Checker) declareTypeParam(name *syntax.Name, scopePos syntax.Pos) *TypeParam {
// Use Typ[Invalid] for the type constraint to ensure that a type
// is present even if the actual constraint has not been assigned
// yet.
// constraints to make sure we don't rely on them if they
// are not properly set yet.
tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
- tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect
- check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
+ tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect
+ check.declare(check.scope, name, tname, scopePos)
return tpar
}
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
obj.color_ = saved
+ // Set the scope's extent to the complete "func (...) { ... }"
+ // so that Scope.Innermost works correctly.
+ sig.scope.pos = fdecl.Pos()
+ sig.scope.end = syntax.EndPos(fdecl)
+
if len(fdecl.TParamList) > 0 && fdecl.Body == nil {
check.softErrorf(fdecl, BadDecl, "generic function is missing function body")
}
case *syntax.FuncLit:
if sig, ok := check.typ(e.Type).(*Signature); ok {
+ // Set the Scope's extent to the complete "func (...) {...}"
+ // so that Scope.Innermost works correctly.
+ sig.scope.pos = e.Pos()
+ sig.scope.end = syntax.EndPos(e)
if !check.conf.IgnoreFuncBodies && e.Body != nil {
// Anonymous functions are considered part of the
// init expression/func declaration which contains
// - the receiver specification acts as local declaration for its type parameters, which may be blank
_, rname, rparams := check.unpackRecv(recvPar.Type, true)
if len(rparams) > 0 {
+ // The scope of the type parameter T in "func (r T[T]) f()"
+ // starts after f, not at "r"; see #52038.
+ scopePos := ftyp.Pos()
tparams := make([]*TypeParam, len(rparams))
for i, rparam := range rparams {
- tparams[i] = check.declareTypeParam(rparam)
+ tparams[i] = check.declareTypeParam(rparam, scopePos)
}
sig.rparams = bindTParams(tparams)
// Blank identifiers don't get declared, so naive type-checking of the
check.collectTypeParams(&sig.tparams, tparams)
}
- // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
- // declarations and then squash that scope into the parent scope (and report any redeclarations at
- // that time).
+ // Use a temporary scope for all parameter declarations and then
+ // squash that scope into the parent scope (and report any
+ // redeclarations at that time).
+ //
+ // TODO(adonovan): now that each declaration has the correct
+ // scopePos, there should be no need for scope squashing.
+ // Audit to ensure all lookups honor scopePos and simplify.
scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
- var recvList []*Var // TODO(gri) remove the need for making a list here
+ scopePos := syntax.EndPos(ftyp) // all parameters' scopes start after the signature
+ var recvList []*Var // TODO(gri) remove the need for making a list here
if recvPar != nil {
- recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false) // use rewritten receiver type, if any
+ recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false, scopePos) // use rewritten receiver type, if any
}
- params, variadic := check.collectParams(scope, ftyp.ParamList, true)
- results, _ := check.collectParams(scope, ftyp.ResultList, false)
+ params, variadic := check.collectParams(scope, ftyp.ParamList, true, scopePos)
+ results, _ := check.collectParams(scope, ftyp.ResultList, false, scopePos)
scope.Squash(func(obj, alt Object) {
var err error_
err.code = DuplicateDecl
// collectParams declares the parameters of list in scope and returns the corresponding
// variable list.
-func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool) (params []*Var, variadic bool) {
+func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool, scopePos syntax.Pos) (params []*Var, variadic bool) {
if list == nil {
return
}
// ok to continue
}
par := NewParam(field.Name.Pos(), check.pkg, name, typ)
- check.declare(scope, field.Name, par, scope.pos)
+ check.declare(scope, field.Name, par, scopePos)
params = append(params, par)
named = true
} else {
check.trace(body.Pos(), "-- %s: %s", name, sig)
}
- // set function scope extent
- sig.scope.pos = body.Pos()
- sig.scope.end = syntax.EndPos(body)
-
// save/restore current environment and set up function environment
// (and use 0 indentation at function start)
defer func(env environment, indent int) {
package types2
-import "cmd/compile/internal/syntax"
+import (
+ "cmd/compile/internal/syntax"
+)
func CmpPos(p, q syntax.Pos) int { return cmpPos(p, q) }
+
+func ScopeComment(s *Scope) string { return s.comment }
+func ObjectScopePos(obj Object) syntax.Pos { return obj.scopePos() }
// scope, the function scopes are embedded in the file scope of the file
// containing the function declaration.
//
+ // The Scope of a function contains the declarations of any
+ // type parameters, parameters, and named results, plus any
+ // local declarations in the body block.
+ // It is coextensive with the complete extent of the
+ // function's syntax ([*ast.FuncDecl] or [*ast.FuncLit]).
+ // The Scopes mapping does not contain an entry for the
+ // function body ([*ast.BlockStmt]); the function's scope is
+ // associated with the [*ast.FuncType].
+ //
// The following node types may appear in Scopes:
//
// *ast.File
var nopos token.Pos
func mustParse(fset *token.FileSet, src string) *ast.File {
- f, err := parser.ParseFile(fset, pkgName(src), src, 0)
+ f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments)
if err != nil {
panic(err) // so we don't need to pass *testing.T
}
type T struct{}
var Y, _ = lib.X, X
-func F(){
+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*/ {
const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
type /*t=undef*/ t /*t=typename:14*/ *t
print(Y) /*Y=var:10*/
x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
- var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
+ var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
var a []int
for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
println(int)
default /*int=var:31*/ :
}
+
+ _ = param1
+ _ = res1
+ return
}
/*main=undef*/
`
_, gotObj := inner.LookupParent(id.Name, id.Pos())
if gotObj != wantObj {
- t.Errorf("%s: got %v, want %v",
- fset.Position(id.Pos()), gotObj, wantObj)
+ // Print the scope tree of mainScope in case of error.
+ var printScopeTree func(indent string, s *Scope)
+ printScopeTree = func(indent string, s *Scope) {
+ t.Logf("%sscope %s %v-%v = %v",
+ indent,
+ ScopeComment(s),
+ s.Pos(),
+ s.End(),
+ s.Names())
+ for i := range s.NumChildren() {
+ printScopeTree(indent+" ", s.Child(i))
+ }
+ }
+ printScopeTree("", mainScope)
+
+ t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
+ fset.Position(id.Pos()),
+ ScopeComment(inner),
+ id.Name,
+ id.Pos(),
+ gotObj,
+ wantObj,
+ ObjectScopePos(wantObj))
continue
}
}
// Declare type parameters up-front, with empty interface as type bound.
// The scope of type parameters starts at the beginning of the type parameter
// list (so we can have mutually recursive parameterized interfaces).
+ scopePos := list.Pos()
for _, f := range list.List {
- tparams = check.declareTypeParams(tparams, f.Names)
+ tparams = check.declareTypeParams(tparams, f.Names, scopePos)
}
// Set the type parameters before collecting the type constraints because
return check.typ(x)
}
-func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam {
+func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident, scopePos token.Pos) []*TypeParam {
// Use Typ[Invalid] for the type constraint to ensure that a type
// is present even if the actual constraint has not been assigned
// yet.
// are not properly set yet.
for _, name := range names {
tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
- tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect
- check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
+ tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect
+ check.declare(check.scope, name, tname, scopePos)
tparams = append(tparams, tpar)
}
check.funcType(sig, fdecl.Recv, fdecl.Type)
obj.color_ = saved
+ // Set the scope's extent to the complete "func (...) { ... }"
+ // so that Scope.Innermost works correctly.
+ sig.scope.pos = fdecl.Pos()
+ sig.scope.end = fdecl.End()
+
if fdecl.Type.TypeParams.NumFields() > 0 && fdecl.Body == nil {
check.softErrorf(fdecl.Name, BadDecl, "generic function is missing function body")
}
/* c => , struct{c int} */
_ = c
}
- _ = func(a, b, c int) /* c => , string */ {
+ _ = func(a, b, c int /* c => , string */) /* c => , int */ {
/* c => , int */
}
_ = c
case *ast.FuncLit:
if sig, ok := check.typ(e.Type).(*Signature); ok {
+ // Set the Scope's extent to the complete "func (...) {...}"
+ // so that Scope.Innermost works correctly.
+ sig.scope.pos = e.Pos()
+ sig.scope.end = e.End()
if !check.conf.IgnoreFuncBodies && e.Body != nil {
// Anonymous functions are considered part of the
// init expression/func declaration which contains
import (
"fmt"
"go/ast"
+ "go/token"
. "internal/types/errors"
)
// - the receiver specification acts as local declaration for its type parameters, which may be blank
_, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true)
if len(rparams) > 0 {
- tparams := check.declareTypeParams(nil, rparams)
+ // The scope of the type parameter T in "func (r T[T]) f()"
+ // starts after f, not at "r"; see #52038.
+ scopePos := ftyp.Params.Pos()
+ tparams := check.declareTypeParams(nil, rparams, scopePos)
sig.rparams = bindTParams(tparams)
// Blank identifiers don't get declared, so naive type-checking of the
// receiver type expression would fail in Checker.collectParams below,
}
}
- // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
- // declarations and then squash that scope into the parent scope (and report any redeclarations at
- // that time).
+ // Use a temporary scope for all parameter declarations and then
+ // squash that scope into the parent scope (and report any
+ // redeclarations at that time).
+ //
+ // TODO(adonovan): now that each declaration has the correct
+ // scopePos, there should be no need for scope squashing.
+ // Audit to ensure all lookups honor scopePos and simplify.
scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
- recvList, _ := check.collectParams(scope, recvPar, false)
- params, variadic := check.collectParams(scope, ftyp.Params, true)
- results, _ := check.collectParams(scope, ftyp.Results, false)
+ scopePos := ftyp.End() // all parameters' scopes start after the signature
+ recvList, _ := check.collectParams(scope, recvPar, false, scopePos)
+ params, variadic := check.collectParams(scope, ftyp.Params, true, scopePos)
+ results, _ := check.collectParams(scope, ftyp.Results, false, scopePos)
scope.squash(func(obj, alt Object) {
check.errorf(obj, DuplicateDecl, "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
// collectParams declares the parameters of list in scope and returns the corresponding
// variable list.
-func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
+func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool, scopePos token.Pos) (params []*Var, variadic bool) {
if list == nil {
return
}
// ok to continue
}
par := NewParam(name.Pos(), check.pkg, name.Name, typ)
- check.declare(scope, name, par, scope.pos)
+ check.declare(scope, name, par, scopePos)
params = append(params, par)
}
named = true
check.trace(body.Pos(), "-- %s: %s", name, sig)
}
- // set function scope extent
- sig.scope.pos = body.Pos()
- sig.scope.end = body.End()
-
// save/restore current environment and set up function environment
// (and use 0 indentation at function start)
defer func(env environment, indent int) {
package types
-import "go/token"
+import (
+ "go/token"
+)
func CmpPos(p, q token.Pos) int { return cmpPos(p, q) }
+
+func ScopeComment(s *Scope) string { return s.comment }
+func ObjectScopePos(obj Object) token.Pos { return obj.scopePos() }