Skip to content

Commit 79dbdf2

Browse files
committed
go/types: add Checker.walkDecl to simplify checking declarations
Handling ast.GenDecls while typechecking is repetitive, and a source of diffs compared to typechecking using the cmd/compile/internal/syntax package, which unpacks declaration groups into individual declarations. Refactor to extract the logic for walking declarations. This introduces a new AST abstraction: types.decl, which comes at some minor performance cost. However, if we are to fully abstract the AST we will be paying this cost anyway, and benchmarking suggests that the cost is negligible. Change-Id: If73c30c3d08053ccf7bf21ef886f0452fdbf142e Reviewed-on: https://go-review.googlesource.com/c/go/+/256298 Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Trust: Robert Findley <[email protected]>
1 parent 44a15a7 commit 79dbdf2

File tree

2 files changed

+275
-269
lines changed

2 files changed

+275
-269
lines changed

src/go/types/decl.go

Lines changed: 154 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,76 @@ func firstInSrc(path []Object) int {
381381
return fst
382382
}
383383

384+
type (
385+
decl interface {
386+
node() ast.Node
387+
}
388+
389+
importDecl struct{ spec *ast.ImportSpec }
390+
constDecl struct {
391+
spec *ast.ValueSpec
392+
iota int
393+
typ ast.Expr
394+
init []ast.Expr
395+
}
396+
varDecl struct{ spec *ast.ValueSpec }
397+
typeDecl struct{ spec *ast.TypeSpec }
398+
funcDecl struct{ decl *ast.FuncDecl }
399+
)
400+
401+
func (d importDecl) node() ast.Node { return d.spec }
402+
func (d constDecl) node() ast.Node { return d.spec }
403+
func (d varDecl) node() ast.Node { return d.spec }
404+
func (d typeDecl) node() ast.Node { return d.spec }
405+
func (d funcDecl) node() ast.Node { return d.decl }
406+
407+
func (check *Checker) walkDecls(decls []ast.Decl, f func(decl)) {
408+
for _, d := range decls {
409+
check.walkDecl(d, f)
410+
}
411+
}
412+
413+
func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
414+
switch d := d.(type) {
415+
case *ast.BadDecl:
416+
// ignore
417+
case *ast.GenDecl:
418+
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
419+
for iota, s := range d.Specs {
420+
switch s := s.(type) {
421+
case *ast.ImportSpec:
422+
f(importDecl{s})
423+
case *ast.ValueSpec:
424+
switch d.Tok {
425+
case token.CONST:
426+
// determine which initialization expressions to use
427+
switch {
428+
case s.Type != nil || len(s.Values) > 0:
429+
last = s
430+
case last == nil:
431+
last = new(ast.ValueSpec) // make sure last exists
432+
}
433+
check.arityMatch(s, last)
434+
f(constDecl{spec: s, iota: iota, init: last.Values, typ: last.Type})
435+
case token.VAR:
436+
check.arityMatch(s, nil)
437+
f(varDecl{s})
438+
default:
439+
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
440+
}
441+
case *ast.TypeSpec:
442+
f(typeDecl{s})
443+
default:
444+
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
445+
}
446+
}
447+
case *ast.FuncDecl:
448+
f(funcDecl{d})
449+
default:
450+
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
451+
}
452+
}
453+
384454
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
385455
assert(obj.typ == nil)
386456

@@ -664,133 +734,105 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
664734
}
665735
}
666736

667-
func (check *Checker) declStmt(decl ast.Decl) {
737+
func (check *Checker) declStmt(d ast.Decl) {
668738
pkg := check.pkg
669739

670-
switch d := decl.(type) {
671-
case *ast.BadDecl:
672-
// ignore
740+
check.walkDecl(d, func(d decl) {
741+
switch d := d.(type) {
742+
case constDecl:
743+
top := len(check.delayed)
673744

674-
case *ast.GenDecl:
675-
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
676-
for iota, spec := range d.Specs {
677-
switch s := spec.(type) {
678-
case *ast.ValueSpec:
679-
switch d.Tok {
680-
case token.CONST:
681-
top := len(check.delayed)
745+
// declare all constants
746+
lhs := make([]*Const, len(d.spec.Names))
747+
for i, name := range d.spec.Names {
748+
obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(d.iota)))
749+
lhs[i] = obj
682750

683-
// determine which init exprs to use
684-
switch {
685-
case s.Type != nil || len(s.Values) > 0:
686-
last = s
687-
case last == nil:
688-
last = new(ast.ValueSpec) // make sure last exists
689-
}
690-
691-
// declare all constants
692-
lhs := make([]*Const, len(s.Names))
693-
for i, name := range s.Names {
694-
obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
695-
lhs[i] = obj
696-
697-
var init ast.Expr
698-
if i < len(last.Values) {
699-
init = last.Values[i]
700-
}
751+
var init ast.Expr
752+
if i < len(d.init) {
753+
init = d.init[i]
754+
}
701755

702-
check.constDecl(obj, last.Type, init)
703-
}
756+
check.constDecl(obj, d.typ, init)
757+
}
704758

705-
check.arityMatch(s, last)
759+
// process function literals in init expressions before scope changes
760+
check.processDelayed(top)
706761

707-
// process function literals in init expressions before scope changes
708-
check.processDelayed(top)
762+
// spec: "The scope of a constant or variable identifier declared
763+
// inside a function begins at the end of the ConstSpec or VarSpec
764+
// (ShortVarDecl for short variable declarations) and ends at the
765+
// end of the innermost containing block."
766+
scopePos := d.spec.End()
767+
for i, name := range d.spec.Names {
768+
check.declare(check.scope, name, lhs[i], scopePos)
769+
}
709770

710-
// spec: "The scope of a constant or variable identifier declared
711-
// inside a function begins at the end of the ConstSpec or VarSpec
712-
// (ShortVarDecl for short variable declarations) and ends at the
713-
// end of the innermost containing block."
714-
scopePos := s.End()
715-
for i, name := range s.Names {
716-
check.declare(check.scope, name, lhs[i], scopePos)
717-
}
771+
case varDecl:
772+
top := len(check.delayed)
718773

719-
case token.VAR:
720-
top := len(check.delayed)
774+
lhs0 := make([]*Var, len(d.spec.Names))
775+
for i, name := range d.spec.Names {
776+
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
777+
}
721778

722-
lhs0 := make([]*Var, len(s.Names))
723-
for i, name := range s.Names {
724-
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
779+
// initialize all variables
780+
for i, obj := range lhs0 {
781+
var lhs []*Var
782+
var init ast.Expr
783+
switch len(d.spec.Values) {
784+
case len(d.spec.Names):
785+
// lhs and rhs match
786+
init = d.spec.Values[i]
787+
case 1:
788+
// rhs is expected to be a multi-valued expression
789+
lhs = lhs0
790+
init = d.spec.Values[0]
791+
default:
792+
if i < len(d.spec.Values) {
793+
init = d.spec.Values[i]
725794
}
726-
727-
// initialize all variables
728-
for i, obj := range lhs0 {
729-
var lhs []*Var
730-
var init ast.Expr
731-
switch len(s.Values) {
732-
case len(s.Names):
733-
// lhs and rhs match
734-
init = s.Values[i]
735-
case 1:
736-
// rhs is expected to be a multi-valued expression
737-
lhs = lhs0
738-
init = s.Values[0]
739-
default:
740-
if i < len(s.Values) {
741-
init = s.Values[i]
742-
}
743-
}
744-
check.varDecl(obj, lhs, s.Type, init)
745-
if len(s.Values) == 1 {
746-
// If we have a single lhs variable we are done either way.
747-
// If we have a single rhs expression, it must be a multi-
748-
// valued expression, in which case handling the first lhs
749-
// variable will cause all lhs variables to have a type
750-
// assigned, and we are done as well.
751-
if debug {
752-
for _, obj := range lhs0 {
753-
assert(obj.typ != nil)
754-
}
755-
}
756-
break
795+
}
796+
check.varDecl(obj, lhs, d.spec.Type, init)
797+
if len(d.spec.Values) == 1 {
798+
// If we have a single lhs variable we are done either way.
799+
// If we have a single rhs expression, it must be a multi-
800+
// valued expression, in which case handling the first lhs
801+
// variable will cause all lhs variables to have a type
802+
// assigned, and we are done as well.
803+
if debug {
804+
for _, obj := range lhs0 {
805+
assert(obj.typ != nil)
757806
}
758807
}
759-
760-
check.arityMatch(s, nil)
761-
762-
// process function literals in init expressions before scope changes
763-
check.processDelayed(top)
764-
765-
// declare all variables
766-
// (only at this point are the variable scopes (parents) set)
767-
scopePos := s.End() // see constant declarations
768-
for i, name := range s.Names {
769-
// see constant declarations
770-
check.declare(check.scope, name, lhs0[i], scopePos)
771-
}
772-
773-
default:
774-
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
808+
break
775809
}
810+
}
776811

777-
case *ast.TypeSpec:
778-
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
779-
// spec: "The scope of a type identifier declared inside a function
780-
// begins at the identifier in the TypeSpec and ends at the end of
781-
// the innermost containing block."
782-
scopePos := s.Name.Pos()
783-
check.declare(check.scope, s.Name, obj, scopePos)
784-
// mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
785-
obj.setColor(grey + color(check.push(obj)))
786-
check.typeDecl(obj, s.Type, nil, s.Assign.IsValid())
787-
check.pop().setColor(black)
788-
default:
789-
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
812+
// process function literals in init expressions before scope changes
813+
check.processDelayed(top)
814+
815+
// declare all variables
816+
// (only at this point are the variable scopes (parents) set)
817+
scopePos := d.spec.End() // see constant declarations
818+
for i, name := range d.spec.Names {
819+
// see constant declarations
820+
check.declare(check.scope, name, lhs0[i], scopePos)
790821
}
791-
}
792822

793-
default:
794-
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
795-
}
823+
case typeDecl:
824+
obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
825+
// spec: "The scope of a type identifier declared inside a function
826+
// begins at the identifier in the TypeSpec and ends at the end of
827+
// the innermost containing block."
828+
scopePos := d.spec.Name.Pos()
829+
check.declare(check.scope, d.spec.Name, obj, scopePos)
830+
// mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
831+
obj.setColor(grey + color(check.push(obj)))
832+
check.typeDecl(obj, d.spec.Type, nil, d.spec.Assign.IsValid())
833+
check.pop().setColor(black)
834+
default:
835+
check.invalidAST(d.node().Pos(), "unknown ast.Decl node %T", d.node())
836+
}
837+
})
796838
}

0 commit comments

Comments
 (0)