@@ -580,13 +580,27 @@ var basicTypeNames = [...]string{
580580// getTypeCodeName returns a name for this type that can be used in the
581581// interface lowering pass to assign type codes as expected by the reflect
582582// package. See getTypeCodeNum.
583- func (c * compilerContext ) getTypeCodeName (t types.Type ) (string , bool ) {
583+ //
584+ // isLocal is true when the type is declared inside a function body.
585+ // 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.
588+ func (c * compilerContext ) getTypeCodeName (t types.Type ) (name string , isLocal bool ) {
584589 switch t := types .Unalias (t ).(type ) {
585590 case * types.Named :
586- if t .Obj ().Parent () != t .Obj ().Pkg ().Scope () {
587- return "named:" + t .String () + "$local" , true
591+ tn := t .Obj ()
592+ if tn .Pkg () == nil || tn .Parent () == tn .Pkg ().Scope () {
593+ // Package-scope or builtin: the printed name is unique.
594+ return "named:" + t .String (), false
595+ }
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" )
588602 }
589- return "named:" + t . String (), false
603+ return "named:" + n , true
590604 case * types.Array :
591605 s , isLocal := c .getTypeCodeName (t .Elem ())
592606 return "array:" + strconv .FormatInt (t .Len (), 10 ) + ":" + s , isLocal
@@ -672,6 +686,274 @@ func (c *compilerContext) getTypeCodeName(t types.Type) (string, bool) {
672686 }
673687}
674688
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:
692+ //
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.
702+ //
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.
709+ //
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.
713+ 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.
719+ 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 ] {
724+ return
725+ }
726+ // fn belongs to an instance subtree if it is itself an
727+ // instantiation or if we reached it from one.
728+ //
729+ // len(TypeArgs()) is used instead of fn.Origin() because
730+ // Origin() may call Build() on fn's declaring package, which
731+ // would defeat per-package compilation.
732+ isInstanceRoot := len (fn .TypeArgs ()) > 0
733+ if ! isInstanceRoot && ! inInstance && fn .Pkg != ssaPkg {
734+ return
735+ }
736+ if fn .Blocks == nil && fn .AnonFuncs == nil {
737+ return
738+ }
739+ seenInstWalk [fn ] = true
740+ isInInstance := inInstance || isInstanceRoot
741+ if isInInstance {
742+ instances = append (instances , fn )
743+ }
744+ for _ , anon := range fn .AnonFuncs {
745+ instWalk (anon , isInInstance )
746+ }
747+ var ops [10 ]* ssa.Value
748+ for _ , b := range fn .Blocks {
749+ for _ , instr := range b .Instrs {
750+ for _ , op := range instr .Operands (ops [:0 ]) {
751+ if op == nil || * op == nil {
752+ continue
753+ }
754+ if callee , ok := (* op ).(* ssa.Function ); ok {
755+ instWalk (callee , isInInstance )
756+ }
757+ }
758+ }
759+ }
760+ }
761+ for _ , member := range ssaPkg .Members {
762+ switch m := member .(type ) {
763+ case * ssa.Function :
764+ instWalk (m , false )
765+ case * ssa.Type :
766+ mset := c .program .MethodSets .MethodSet (m .Type ())
767+ for i := 0 ; i < mset .Len (); i ++ {
768+ instWalk (c .program .MethodValue (mset .At (i )), false )
769+ }
770+ pmset := c .program .MethodSets .MethodSet (types .NewPointer (m .Type ()))
771+ for i := 0 ; i < pmset .Len (); i ++ {
772+ instWalk (c .program .MethodValue (pmset .At (i )), false )
773+ }
774+ }
775+ }
776+
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
821+ // 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 )
833+ for _ , fn := range instances {
834+ c .registerLocalTypes (fn , true )
835+ }
836+ for _ , fn := range packageFuncs {
837+ c .registerLocalTypes (fn , false )
838+ }
839+ }
840+
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.
846+ //
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
855+ seen := map [types.Type ]bool {}
856+ var visit func (t types.Type )
857+ visit = func (t types.Type ) {
858+ if t == nil || seen [t ] {
859+ return
860+ }
861+ seen [t ] = true
862+ switch t := t .(type ) {
863+ case * types.Alias :
864+ visit (types .Unalias (t ))
865+ case * types.Named :
866+ 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 )
871+ }
872+ }
873+ targs := t .TypeArgs ()
874+ for i := 0 ; i < targs .Len (); i ++ {
875+ visit (targs .At (i ))
876+ }
877+ visit (t .Underlying ())
878+ case * types.Pointer :
879+ visit (t .Elem ())
880+ case * types.Slice :
881+ visit (t .Elem ())
882+ case * types.Array :
883+ visit (t .Elem ())
884+ case * types.Chan :
885+ visit (t .Elem ())
886+ case * types.Map :
887+ visit (t .Key ())
888+ visit (t .Elem ())
889+ case * types.Struct :
890+ for i := 0 ; i < t .NumFields (); i ++ {
891+ visit (t .Field (i ).Type ())
892+ }
893+ case * types.Signature :
894+ if p := t .Params (); p != nil {
895+ for i := 0 ; i < p .Len (); i ++ {
896+ visit (p .At (i ).Type ())
897+ }
898+ }
899+ if r := t .Results (); r != nil {
900+ for i := 0 ; i < r .Len (); i ++ {
901+ visit (r .At (i ).Type ())
902+ }
903+ }
904+ case * types.Tuple :
905+ for i := 0 ; i < t .Len (); i ++ {
906+ visit (t .At (i ).Type ())
907+ }
908+ 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
913+ // cycles formed by methods that mention the interface
914+ // itself.
915+ for i := 0 ; i < t .NumMethods (); i ++ {
916+ visit (t .Method (i ).Type ())
917+ }
918+ }
919+ }
920+ for _ , p := range fn .Params {
921+ visit (p .Type ())
922+ }
923+ for _ , fv := range fn .FreeVars {
924+ visit (fv .Type ())
925+ }
926+ for _ , l := range fn .Locals {
927+ visit (l .Type ())
928+ }
929+ var ops [10 ]* ssa.Value
930+ for _ , b := range fn .Blocks {
931+ for _ , instr := range b .Instrs {
932+ if v , ok := instr .(ssa.Value ); ok {
933+ visit (v .Type ())
934+ }
935+ for _ , op := range instr .Operands (ops [:0 ]) {
936+ if op != nil && * op != nil {
937+ visit ((* op ).Type ())
938+ }
939+ }
940+ }
941+ }
942+ if len (found ) == 0 {
943+ return
944+ }
945+ // Sort by raw token.Pos: this gives a total order on declarations
946+ // that is stable across builds and unaffected by //line directives
947+ // (which only adjust the human-facing position from Fset.Position).
948+ sort .Slice (found , func (i , j int ) bool {
949+ return found [i ].Pos () < found [j ].Pos ()
950+ })
951+ enclosing := fn .RelString (nil )
952+ for i , tn := range found {
953+ c .localTypeNames [tn ] = enclosing + "." + tn .Name () + "$" + strconv .Itoa (i + 1 )
954+ }
955+ }
956+
675957// getTypeMethodSet returns a reference (GEP) to a global method set. This
676958// method set should be unreferenced after the interface lowering pass.
677959func (c * compilerContext ) getTypeMethodSet (typ types.Type ) llvm.Value {
0 commit comments