From 8eeb1bff1d7107828b41af08e599c78fc36bab30 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Mon, 23 Aug 2021 15:45:10 -0700 Subject: [PATCH] cmd/compile: reuse same node for global dictionaries Change stencil.go:getDictionaryValue() and reflect.go:getDictionary() to reuse any existing name node that has been created for the needed global dictionary. Otherwise, these functions may set the Def on a specific dictionary sym to two different name nodes, which means the first node will not satisfy the invariant 'n.Sym().Def.(*ir.Name) == n' (which is the assertion in this issue). Fixes #47896 Change-Id: I1e7ae1efd077a83c7878b4342feb6d28d52476cc Reviewed-on: https://go-review.googlesource.com/c/go/+/344609 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Dan Scales --- src/cmd/compile/internal/noder/stencil.go | 17 +++-- .../compile/internal/reflectdata/reflect.go | 17 +++-- test/typeparam/issue47896.go | 74 +++++++++++++++++++ 3 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 test/typeparam/issue47896.go diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 2d275d6a3b2c1..570dec9990454 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -1579,12 +1579,17 @@ func (g *irgen) finalizeSyms() { func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node { sym := g.getDictionarySym(gf, targs, isMeth) - // Make a node referencing the dictionary symbol. - n := typecheck.NewName(sym) - n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter - n.SetTypecheck(1) - n.Class = ir.PEXTERN - sym.Def = n + // Make (or reuse) a node referencing the dictionary symbol. + var n *ir.Name + if sym.Def != nil { + n = sym.Def.(*ir.Name) + } else { + n = typecheck.NewName(sym) + n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter + n.SetTypecheck(1) + n.Class = ir.PEXTERN + sym.Def = n + } // Return the address of the dictionary. np := typecheck.NodAddr(n) diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 3ba8f52541edc..a95c76ff268f0 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -2047,12 +2047,17 @@ func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node { base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name) } - // Make a node referencing the dictionary symbol. - n := typecheck.NewName(sym) - n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter - n.SetTypecheck(1) - n.Class = ir.PEXTERN - sym.Def = n + // Make (or reuse) a node referencing the dictionary symbol. + var n *ir.Name + if sym.Def != nil { + n = sym.Def.(*ir.Name) + } else { + n = typecheck.NewName(sym) + n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter + n.SetTypecheck(1) + n.Class = ir.PEXTERN + sym.Def = n + } // Return the address of the dictionary. np := typecheck.NodAddr(n) diff --git a/test/typeparam/issue47896.go b/test/typeparam/issue47896.go new file mode 100644 index 0000000000000..1b2f265cc1121 --- /dev/null +++ b/test/typeparam/issue47896.go @@ -0,0 +1,74 @@ +// compile -G=3 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "database/sql" +) + +// Collection generic interface which things can be added to. +type Collection[T any] interface { + Add(T) +} + +// Slice generic slice implementation of a Collection +type Slice[T any] []*T + +func (s *Slice[T]) Add(t *T) { + *s = append(*s, t) +} + +type Scanner interface { + Scan(...interface{}) error +} + +type Mapper[T any] func(s Scanner, t T) error + +type Repository[T any] struct { + db *sql.DB +} + +func (r *Repository[T]) scan(rows *sql.Rows, m Mapper[*T], c Collection[*T]) error { + for rows.Next() { + t := new(T) + if err := m(rows, t); err != nil { + return err + } + c.Add(t) + } + return rows.Err() +} + +func (r *Repository[T]) query(query string, m Mapper[*T], c Collection[*T]) error { + rows, err := r.db.Query(query) + if err != nil { + return err + } + if err := r.scan(rows, m, c); err != nil { + rows.Close() + return err + } + return rows.Close() +} + +type Actor struct { + ActorID uint16 + FirstName string + LastName string +} + +type ActorRepository struct { + r Repository[Actor] +} + +func (ActorRepository) scan(s Scanner, a *Actor) error { + return s.Scan(&a.ActorID, &a.FirstName, &a.LastName) +} + +func (r *ActorRepository) SelectAll(c Collection[*Actor]) error { + return r.r.query("SELECT `actor_id`, `first_name`, `last_name` FROM `actor` LIMIT 10", r.scan, c) +}