Skip to content

Commit d0cf28b

Browse files
committed
Kill off the second pass
1 parent d05e0b7 commit d0cf28b

2 files changed

Lines changed: 106 additions & 138 deletions

File tree

compiler/compiler.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,22 +92,21 @@ type compilerContext struct {
9292
pkg *types.Package
9393
packageDir string // directory for this package
9494
runtimePkg *types.Package
95-
localTypeNames map[*types.TypeName]string
95+
localTypeNames typeutil.Map // *types.Named (synthetic local from generic instantiation) -> string
9696
}
9797

9898
// newCompilerContext returns a new compiler context ready for use, most
9999
// importantly with a newly created LLVM context and module.
100100
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool) *compilerContext {
101101
c := &compilerContext{
102-
Config: config,
103-
DumpSSA: dumpSSA,
104-
difiles: make(map[string]llvm.Metadata),
105-
ditypes: make(map[types.Type]llvm.Metadata),
106-
machine: machine,
107-
targetData: machine.CreateTargetData(),
108-
functionInfos: map[*ssa.Function]functionInfo{},
109-
astComments: map[string]*ast.CommentGroup{},
110-
localTypeNames: map[*types.TypeName]string{},
102+
Config: config,
103+
DumpSSA: dumpSSA,
104+
difiles: make(map[string]llvm.Metadata),
105+
ditypes: make(map[types.Type]llvm.Metadata),
106+
machine: machine,
107+
targetData: machine.CreateTargetData(),
108+
functionInfos: map[*ssa.Function]functionInfo{},
109+
astComments: map[string]*ast.CommentGroup{},
111110
}
112111

113112
c.ctx = llvm.NewContext()

compiler/interface.go

Lines changed: 97 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"go/token"
1212
"go/types"
13+
"path/filepath"
1314
"sort"
1415
"strconv"
1516
"strings"
@@ -583,8 +584,19 @@ var basicTypeNames = [...]string{
583584
//
584585
// isLocal is true when the type is declared inside a function body.
585586
// Such types need a per-declaration (or per instantiation) suffix
586-
// because their printed names are not unique; scanLocalTypes assigns
587-
// the suffix and stores the result in c.localTypeNames.
587+
// because their printed names are not unique.
588+
//
589+
// Ordinary function-local types (TypeName.Parent() != nil) are
590+
// disambiguated lazily from their declaration position: every
591+
// declaration in the package has a distinct (file, line, column)
592+
// triple, and the position is taken un-//line-adjusted so it is
593+
// stable across builds. Such types are only nameable inside their
594+
// declaring package, so the name does not need to agree with anything
595+
// computed in another package.
596+
//
597+
// Synthetic locals (TypeName.Parent() == nil), produced by generic
598+
// instantiation, are pre-registered by scanLocalTypes because their
599+
// names must agree across packages that materialize the same instance.
588600
func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bool) {
589601
switch t := types.Unalias(t).(type) {
590602
case *types.Named:
@@ -593,14 +605,19 @@ func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bo
593605
// Package-scope or builtin: the printed name is unique.
594606
return "named:" + t.String(), false
595607
}
596-
// Function-local type. Both ordinary locals (Parent() != nil)
597-
// and synthetic locals from generic instantiation
598-
// (Parent() == nil) are pre-registered by scanLocalTypes.
599-
n, ok := c.localTypeNames[tn]
600-
if !ok {
601-
panic("compiler: local type " + tn.Name() + " was not registered by scanLocalTypes")
608+
if tn.Parent() != nil {
609+
// Ordinary function-local type. Use the un-//line-adjusted
610+
// declaration position as the disambiguator.
611+
pos := c.program.Fset.PositionFor(tn.Pos(), false)
612+
return "named:" + t.String() + "$" + filepath.Base(pos.Filename) + ":" + strconv.Itoa(pos.Line) + ":" + strconv.Itoa(pos.Column), true
602613
}
603-
return "named:" + n, true
614+
// Synthetic local from generic instantiation: must have been
615+
// pre-registered by scanLocalTypes.
616+
v := c.localTypeNames.At(t)
617+
if v == nil {
618+
panic("compiler: synthetic local type " + tn.Name() + " was not registered by scanLocalTypes")
619+
}
620+
return "named:" + v.(string), true
604621
case *types.Array:
605622
s, isLocal := c.getTypeCodeName(t.Elem())
606623
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + s, isLocal
@@ -686,41 +703,40 @@ func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bo
686703
}
687704
}
688705

689-
// scanLocalTypes assigns names to every function-local named type in
690-
// the package and stores them in c.localTypeNames. Two flavors are
691-
// handled:
706+
// scanLocalTypes assigns names to every synthetic *types.TypeName
707+
// (TypeName.Parent() == nil) reachable from this package and stores
708+
// them in c.localTypeNames.
692709
//
693-
// 1. Synthetic TypeNames produced by generic instantiation
694-
// (TypeName.Parent() == nil). Two instantiations of the same
695-
// generic function (e.g. F[int] and F[string]) produce TypeNames
696-
// with the same printed name and the same source position, so
697-
// each one is named with the enclosing instance's RelString as
698-
// prefix. RelString encodes the type arguments, matching Go's
699-
// runtime behavior, where F[int].Inner and F[string].Inner are
700-
// distinct types even when Inner does not mention the type
701-
// parameter.
710+
// Synthetic TypeNames are produced by generic instantiation: two
711+
// instantiations of the same generic function (e.g. F[int] and
712+
// F[string]) produce TypeNames with the same printed name and the
713+
// same source position, so each is named with the enclosing
714+
// instance's RelString as prefix. RelString encodes the type
715+
// arguments, matching Go's runtime behavior, where F[int].Inner and
716+
// F[string].Inner are distinct types even when Inner does not mention
717+
// the type parameter.
702718
//
703-
// 2. Ordinary function-local TypeNames (TypeName.Parent() != nil).
704-
// Each one is named with its declaring function's RelString plus
705-
// a per-function counter assigned in source order. This mirrors
706-
// the ·N suffix the standard Go compiler uses for such types and
707-
// is robust against //line directives that would otherwise make a
708-
// file:line:column suffix non-unique.
719+
// A given instance may be materialized by several packages (the body
720+
// of F[int] is compiled in every package that calls F[int]); its
721+
// reflect/types.type:* global has LinkOnceODRLinkage and is merged by
722+
// name at link time. The chosen name therefore depends only on
723+
// intrinsic SSA properties (RelString and the raw token.Pos used as a
724+
// sort key), so any package compiling the same instance produces the
725+
// same identifier.
709726
//
710-
// Names depend only on intrinsic SSA properties (RelString and the
711-
// raw token.Pos used as a sort key), so any package compiling the
712-
// same function or instance produces the same identifier.
727+
// Ordinary function-local TypeNames (TypeName.Parent() != nil) are
728+
// not handled here: they are nameable only inside their declaring
729+
// package, and getTypeCodeName derives a stable per-declaration name
730+
// for them directly from their source position.
713731
func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
714-
// Pass 1: locate every generic instance reachable from this
715-
// package (including instances declared in imported packages and
716-
// any function reached through an instance subtree). Synthetic
717-
// TypeNames are produced by instantiation, so we need the call
718-
// graph to find them all.
732+
// Locate every generic instance reachable from this package
733+
// (including instances declared in imported packages and any
734+
// function reached through an instance subtree).
719735
var instances []*ssa.Function
720-
seenInstWalk := map[*ssa.Function]bool{}
721-
var instWalk func(fn *ssa.Function, inInstance bool)
722-
instWalk = func(fn *ssa.Function, inInstance bool) {
723-
if fn == nil || seenInstWalk[fn] {
736+
seen := map[*ssa.Function]bool{}
737+
var walk func(fn *ssa.Function, inInstance bool)
738+
walk = func(fn *ssa.Function, inInstance bool) {
739+
if fn == nil || seen[fn] {
724740
return
725741
}
726742
// fn belongs to an instance subtree if it is itself an
@@ -736,13 +752,13 @@ func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
736752
if fn.Blocks == nil && fn.AnonFuncs == nil {
737753
return
738754
}
739-
seenInstWalk[fn] = true
755+
seen[fn] = true
740756
isInInstance := inInstance || isInstanceRoot
741757
if isInInstance {
742758
instances = append(instances, fn)
743759
}
744760
for _, anon := range fn.AnonFuncs {
745-
instWalk(anon, isInInstance)
761+
walk(anon, isInInstance)
746762
}
747763
var ops [10]*ssa.Value
748764
for _, b := range fn.Blocks {
@@ -752,7 +768,7 @@ func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
752768
continue
753769
}
754770
if callee, ok := (*op).(*ssa.Function); ok {
755-
instWalk(callee, isInInstance)
771+
walk(callee, isInInstance)
756772
}
757773
}
758774
}
@@ -761,97 +777,46 @@ func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
761777
for _, member := range ssaPkg.Members {
762778
switch m := member.(type) {
763779
case *ssa.Function:
764-
instWalk(m, false)
780+
walk(m, false)
765781
case *ssa.Type:
766782
mset := c.program.MethodSets.MethodSet(m.Type())
767783
for i := 0; i < mset.Len(); i++ {
768-
instWalk(c.program.MethodValue(mset.At(i)), false)
784+
walk(c.program.MethodValue(mset.At(i)), false)
769785
}
770786
pmset := c.program.MethodSets.MethodSet(types.NewPointer(m.Type()))
771787
for i := 0; i < pmset.Len(); i++ {
772-
instWalk(c.program.MethodValue(pmset.At(i)), false)
788+
walk(c.program.MethodValue(pmset.At(i)), false)
773789
}
774790
}
775791
}
776792

777-
// Pass 2: collect every non-instance function defined in this
778-
// package together with its closures. Ordinary function-local
779-
// TypeNames are scoped to their declaring function (and visible
780-
// in nested closures only), so the declaring function is always
781-
// somewhere in this lexical tree. Following callees here would
782-
// be wrong: an instantiated function whose substituted signature
783-
// mentions the local type would otherwise race with the actual
784-
// declaring function for ownership.
785-
var packageFuncs []*ssa.Function
786-
var collect func(fn *ssa.Function)
787-
collect = func(fn *ssa.Function) {
788-
if fn == nil || fn.Pkg != ssaPkg {
789-
return
790-
}
791-
if len(fn.TypeArgs()) > 0 {
792-
// Generic instances are handled by pass 1 (their local
793-
// types are synthetic).
794-
return
795-
}
796-
if fn.Blocks == nil && fn.AnonFuncs == nil {
797-
return
798-
}
799-
packageFuncs = append(packageFuncs, fn)
800-
for _, anon := range fn.AnonFuncs {
801-
collect(anon)
802-
}
803-
}
804-
for _, member := range ssaPkg.Members {
805-
switch m := member.(type) {
806-
case *ssa.Function:
807-
collect(m)
808-
case *ssa.Type:
809-
mset := c.program.MethodSets.MethodSet(m.Type())
810-
for i := 0; i < mset.Len(); i++ {
811-
collect(c.program.MethodValue(mset.At(i)))
812-
}
813-
pmset := c.program.MethodSets.MethodSet(types.NewPointer(m.Type()))
814-
for i := 0; i < pmset.Len(); i++ {
815-
collect(c.program.MethodValue(pmset.At(i)))
816-
}
817-
}
818-
}
819-
820-
// Registration is first-writer-wins, so visit each list in a
793+
// Registration is first-writer-wins (a synthetic TypeName may be
794+
// reachable from several instances), so visit instances in a
821795
// deterministic order. Pos() is a defensive tiebreaker.
822-
sortFns := func(fns []*ssa.Function) {
823-
sort.Slice(fns, func(i, j int) bool {
824-
ri, rj := fns[i].RelString(nil), fns[j].RelString(nil)
825-
if ri != rj {
826-
return ri < rj
827-
}
828-
return fns[i].Pos() < fns[j].Pos()
829-
})
830-
}
831-
sortFns(instances)
832-
sortFns(packageFuncs)
796+
sort.Slice(instances, func(i, j int) bool {
797+
ri, rj := instances[i].RelString(nil), instances[j].RelString(nil)
798+
if ri != rj {
799+
return ri < rj
800+
}
801+
return instances[i].Pos() < instances[j].Pos()
802+
})
833803
for _, fn := range instances {
834-
c.registerLocalTypes(fn, true)
835-
}
836-
for _, fn := range packageFuncs {
837-
c.registerLocalTypes(fn, false)
804+
c.registerSyntheticLocalTypes(fn)
838805
}
839806
}
840807

841-
// registerLocalTypes walks every type reachable from fn's body and
842-
// records each function-local TypeName whose Parent() matches the
843-
// synthetic flag (Parent() == nil for synthetic, != nil otherwise) in
844-
// c.localTypeNames. Each TypeName is named with fn.RelString as the
845-
// owning function plus a per-function counter assigned in source order.
808+
// registerSyntheticLocalTypes walks every type reachable from fn's
809+
// body and records each synthetic *types.Named (TypeName.Parent() ==
810+
// nil) in c.localTypeNames. Each is named with fn.RelString as the
811+
// owning function plus a per-function counter assigned in source
812+
// order.
846813
//
847-
// First-writer-wins: a TypeName already present in c.localTypeNames
848-
// is left alone. The slot is reserved with an empty string during
849-
// collection so later registerLocalTypes calls (within the same
850-
// scanLocalTypes invocation) skip it; the final name is filled in
851-
// after sorting, before scanLocalTypes returns and any
852-
// getTypeCodeName lookups happen.
853-
func (c *compilerContext) registerLocalTypes(fn *ssa.Function, synthetic bool) {
854-
var found []*types.TypeName
814+
// First-writer-wins: a *types.Named already present in
815+
// c.localTypeNames is left alone, so a synthetic type reachable from
816+
// several instances keeps the name assigned by the first (in
817+
// scanLocalTypes' deterministic order).
818+
func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
819+
var found []*types.Named
855820
seen := map[types.Type]bool{}
856821
var visit func(t types.Type)
857822
visit = func(t types.Type) {
@@ -864,10 +829,14 @@ func (c *compilerContext) registerLocalTypes(fn *ssa.Function, synthetic bool) {
864829
visit(types.Unalias(t))
865830
case *types.Named:
866831
tn := t.Obj()
867-
if tn.Pkg() != nil && (tn.Parent() == nil) == synthetic {
868-
if _, ok := c.localTypeNames[tn]; !ok {
869-
c.localTypeNames[tn] = ""
870-
found = append(found, tn)
832+
if tn.Pkg() != nil && tn.Parent() == nil {
833+
if c.localTypeNames.At(t) == nil {
834+
// Reserve the slot so later calls within this
835+
// scanLocalTypes invocation skip it; the final
836+
// name is filled in after sorting, before any
837+
// getTypeCodeName lookups happen.
838+
c.localTypeNames.Set(t, "")
839+
found = append(found, t)
871840
}
872841
}
873842
targs := t.TypeArgs()
@@ -906,10 +875,10 @@ func (c *compilerContext) registerLocalTypes(fn *ssa.Function, synthetic bool) {
906875
visit(t.At(i).Type())
907876
}
908877
case *types.Interface:
909-
// A local type can be reachable only through a local
910-
// interface's method signature, so descend into them.
911-
// getTypeCodeName encodes those signatures into the
912-
// interface's identifier, and the seen map breaks
878+
// A synthetic local type can be reachable only through a
879+
// local interface's method signature, so descend into
880+
// them. getTypeCodeName encodes those signatures into
881+
// the interface's identifier, and the seen map breaks
913882
// cycles formed by methods that mention the interface
914883
// itself.
915884
for i := 0; i < t.NumMethods(); i++ {
@@ -946,11 +915,11 @@ func (c *compilerContext) registerLocalTypes(fn *ssa.Function, synthetic bool) {
946915
// that is stable across builds and unaffected by //line directives
947916
// (which only adjust the human-facing position from Fset.Position).
948917
sort.Slice(found, func(i, j int) bool {
949-
return found[i].Pos() < found[j].Pos()
918+
return found[i].Obj().Pos() < found[j].Obj().Pos()
950919
})
951920
enclosing := fn.RelString(nil)
952-
for i, tn := range found {
953-
c.localTypeNames[tn] = enclosing + "." + tn.Name() + "$" + strconv.Itoa(i+1)
921+
for i, named := range found {
922+
c.localTypeNames.Set(named, enclosing+"."+named.Obj().Name()+"$"+strconv.Itoa(i+1))
954923
}
955924
}
956925

0 commit comments

Comments
 (0)