Skip to content

Commit

Permalink
cmd/compile: reuse same node for global dictionaries
Browse files Browse the repository at this point in the history
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 <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
  • Loading branch information
danscales committed Aug 24, 2021
1 parent be1a693 commit 8eeb1bf
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 12 deletions.
17 changes: 11 additions & 6 deletions src/cmd/compile/internal/noder/stencil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 11 additions & 6 deletions src/cmd/compile/internal/reflectdata/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
74 changes: 74 additions & 0 deletions test/typeparam/issue47896.go
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 8eeb1bf

Please sign in to comment.