@@ -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.
588600func (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.
713731func (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