]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: Fix handling of Name nodes during stenciling
authorDan Scales <danscales@google.com>
Wed, 12 May 2021 02:29:10 +0000 (19:29 -0700)
committerDan Scales <danscales@google.com>
Mon, 24 May 2021 22:16:09 +0000 (22:16 +0000)
The name substitution for stenciling was incorrectly handling non-local
names. Made changes to explicitly built the vars[] name substitution map
based on the local variables (similar to what inlining substitution
does). Then, we we are stenciling a name node, we do NOT make a copy of
the name node if it is not in vars[], since it is then a reference to an
external name. Added new function localvar() to create the new nodes for
the local variables and put them in the vars[] map.

New test listimp2.go, added missing test calls in list2.go

Change-Id: I8946478250c7bf2bd31c3247089bd50cfeeda0fd
Reviewed-on: https://go-review.googlesource.com/c/go/+/322190
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/noder/stencil.go
test/typeparam/list2.go
test/typeparam/listimp2.dir/a.go [new file with mode: 0644]
test/typeparam/listimp2.dir/main.go [new file with mode: 0644]
test/typeparam/listimp2.go [new file with mode: 0644]

index 1626ab9dd313c53599949961d83339db82ba479e..67580add735fe8a6a0002aaa6f2e826beed879d8 100644 (file)
@@ -289,7 +289,7 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
 
        newf.Dcl = make([]*ir.Name, len(gf.Dcl))
        for i, n := range gf.Dcl {
-               newf.Dcl[i] = subst.node(n).(*ir.Name)
+               newf.Dcl[i] = subst.localvar(n)
        }
 
        // Replace the types in the function signature.
@@ -315,9 +315,28 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
        return newf
 }
 
-// node is like DeepCopy(), but creates distinct ONAME nodes, and also descends
-// into closures. It substitutes type arguments for type parameters in all the new
-// nodes.
+// localvar creates a new name node for the specified local variable and enters it
+// in subst.vars. It substitutes type arguments for type parameters in the type of
+// name as needed.
+func (subst *subster) localvar(name *ir.Name) *ir.Name {
+       m := ir.NewNameAt(name.Pos(), name.Sym())
+       if name.IsClosureVar() {
+               m.SetIsClosureVar(true)
+       }
+       m.SetType(subst.typ(name.Type()))
+       m.BuiltinOp = name.BuiltinOp
+       m.Curfn = subst.newf
+       m.Class = name.Class
+       assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC)
+       m.Func = name.Func
+       subst.vars[name] = m
+       m.SetTypecheck(1)
+       return m
+}
+
+// node is like DeepCopy(), but substitutes ONAME nodes based on subst.vars, and
+// also descends into closures. It substitutes type arguments for type parameters
+// in all the new nodes.
 func (subst *subster) node(n ir.Node) ir.Node {
        // Use closure to capture all state needed by the ir.EditChildren argument.
        var edit func(ir.Node) ir.Node
@@ -327,28 +346,10 @@ func (subst *subster) node(n ir.Node) ir.Node {
                        return ir.TypeNode(subst.typ(x.Type()))
 
                case ir.ONAME:
-                       name := x.(*ir.Name)
-                       if v := subst.vars[name]; v != nil {
+                       if v := subst.vars[x.(*ir.Name)]; v != nil {
                                return v
                        }
-                       m := ir.NewNameAt(name.Pos(), name.Sym())
-                       if name.IsClosureVar() {
-                               m.SetIsClosureVar(true)
-                       }
-                       t := x.Type()
-                       if t == nil {
-                               assert(name.BuiltinOp != 0)
-                       } else {
-                               newt := subst.typ(t)
-                               m.SetType(newt)
-                       }
-                       m.BuiltinOp = name.BuiltinOp
-                       m.Curfn = subst.newf
-                       m.Class = name.Class
-                       m.Func = name.Func
-                       subst.vars[name] = m
-                       m.SetTypecheck(1)
-                       return m
+                       return x
                case ir.OLITERAL, ir.ONIL:
                        if x.Sym() != nil {
                                return x
@@ -545,7 +546,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
 func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
        s := make([]*ir.Name, len(l))
        for i, n := range l {
-               s[i] = subst.node(n).(*ir.Name)
+               s[i] = subst.localvar(n)
                if n.Defn != nil {
                        s[i].Defn = subst.node(n.Defn)
                }
index 385193d87657c7ab1172e2bd5170ed3461f377ba..32023cf319d49aa2394a986cb349a5a1b23772bc 100644 (file)
@@ -597,5 +597,14 @@ func TestTransform() {
 
 func main() {
        TestList()
+       TestExtending()
+       TestRemove()
+       TestIssue4103()
+       TestIssue6349()
+       TestMove()
+       TestZeroList()
+       TestInsertBeforeUnknownMark()
+       TestInsertAfterUnknownMark()
+       TestTransform()
 }
 
diff --git a/test/typeparam/listimp2.dir/a.go b/test/typeparam/listimp2.dir/a.go
new file mode 100644 (file)
index 0000000..76ad669
--- /dev/null
@@ -0,0 +1,298 @@
+// 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 a
+
+import (
+       "fmt"
+)
+
+// Element is an element of a linked list.
+type Element[T any] struct {
+       // Next and previous pointers in the doubly-linked list of elements.
+       // To simplify the implementation, internally a list l is implemented
+       // as a ring, such that &l.root is both the next element of the last
+       // list element (l.Back()) and the previous element of the first list
+       // element (l.Front()).
+       next, prev *Element[T]
+
+       // The list to which this element belongs.
+       list *List[T]
+
+       // The value stored with this element.
+       Value T
+}
+
+// Next returns the next list element or nil.
+func (e *Element[T]) Next() *Element[T] {
+       if p := e.next; e.list != nil && p != &e.list.root {
+               return p
+       }
+       return nil
+}
+
+// Prev returns the previous list element or nil.
+func (e *Element[T]) Prev() *Element[T] {
+       if p := e.prev; e.list != nil && p != &e.list.root {
+               return p
+       }
+       return nil
+}
+
+// List represents a doubly linked list.
+// The zero value for List is an empty list ready to use.
+type List[T any] struct {
+       root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
+       len  int     // current list length excluding (this) sentinel element
+}
+
+// Init initializes or clears list l.
+func (l *List[T]) Init() *List[T] {
+       l.root.next = &l.root
+       l.root.prev = &l.root
+       l.len = 0
+       return l
+}
+
+// New returns an initialized list.
+func New[T any]() *List[T] { return new(List[T]).Init() }
+
+// Len returns the number of elements of list l.
+// The complexity is O(1).
+func (l *List[_]) Len() int { return l.len }
+
+// Front returns the first element of list l or nil if the list is empty.
+func (l *List[T]) Front() *Element[T] {
+       if l.len == 0 {
+               return nil
+       }
+       return l.root.next
+}
+
+// Back returns the last element of list l or nil if the list is empty.
+func (l *List[T]) Back() *Element[T] {
+       if l.len == 0 {
+               return nil
+       }
+       return l.root.prev
+}
+
+// lazyInit lazily initializes a zero List value.
+func (l *List[_]) lazyInit() {
+       if l.root.next == nil {
+               l.Init()
+       }
+}
+
+// insert inserts e after at, increments l.len, and returns e.
+func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
+       e.prev = at
+       e.next = at.next
+       e.prev.next = e
+       e.next.prev = e
+       e.list = l
+       l.len++
+       return e
+}
+
+// insertValue is a convenience wrapper for insert(&Element[T]{Value: v}, at).
+func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] {
+       return l.insert(&Element[T]{Value: v}, at)
+}
+
+// remove removes e from its list, decrements l.len, and returns e.
+func (l *List[T]) remove(e *Element[T]) *Element[T] {
+       e.prev.next = e.next
+       e.next.prev = e.prev
+       e.next = nil // avoid memory leaks
+       e.prev = nil // avoid memory leaks
+       e.list = nil
+       l.len--
+       return e
+}
+
+// move moves e to next to at and returns e.
+func (l *List[T]) move(e, at *Element[T]) *Element[T] {
+       if e == at {
+               return e
+       }
+       e.prev.next = e.next
+       e.next.prev = e.prev
+
+       e.prev = at
+       e.next = at.next
+       e.prev.next = e
+       e.next.prev = e
+
+       return e
+}
+
+// Remove removes e from l if e is an element of list l.
+// It returns the element value e.Value.
+// The element must not be nil.
+func (l *List[T]) Remove(e *Element[T]) T {
+       if e.list == l {
+               // if e.list == l, l must have been initialized when e was inserted
+               // in l or l == nil (e is a zero Element) and l.remove will crash
+               l.remove(e)
+       }
+       return e.Value
+}
+
+// PushFront inserts a new element e with value v at the front of list l and returns e.
+func (l *List[T]) PushFront(v T) *Element[T] {
+       l.lazyInit()
+       return l.insertValue(v, &l.root)
+}
+
+// PushBack inserts a new element e with value v at the back of list l and returns e.
+func (l *List[T]) PushBack(v T) *Element[T] {
+       l.lazyInit()
+       return l.insertValue(v, l.root.prev)
+}
+
+// InsertBefore inserts a new element e with value v immediately before mark and returns e.
+// If mark is not an element of l, the list is not modified.
+// The mark must not be nil.
+func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
+       if mark.list != l {
+               return nil
+       }
+       // see comment in List.Remove about initialization of l
+       return l.insertValue(v, mark.prev)
+}
+
+// InsertAfter inserts a new element e with value v immediately after mark and returns e.
+// If mark is not an element of l, the list is not modified.
+// The mark must not be nil.
+func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
+       if mark.list != l {
+               return nil
+       }
+       // see comment in List.Remove about initialization of l
+       return l.insertValue(v, mark)
+}
+
+// MoveToFront moves element e to the front of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *List[T]) MoveToFront(e *Element[T]) {
+       if e.list != l || l.root.next == e {
+               return
+       }
+       // see comment in List.Remove about initialization of l
+       l.move(e, &l.root)
+}
+
+// MoveToBack moves element e to the back of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *List[T]) MoveToBack(e *Element[T]) {
+       if e.list != l || l.root.prev == e {
+               return
+       }
+       // see comment in List.Remove about initialization of l
+       l.move(e, l.root.prev)
+}
+
+// MoveBefore moves element e to its new position before mark.
+// If e or mark is not an element of l, or e == mark, the list is not modified.
+// The element and mark must not be nil.
+func (l *List[T]) MoveBefore(e, mark *Element[T]) {
+       if e.list != l || e == mark || mark.list != l {
+               return
+       }
+       l.move(e, mark.prev)
+}
+
+// MoveAfter moves element e to its new position after mark.
+// If e or mark is not an element of l, or e == mark, the list is not modified.
+// The element and mark must not be nil.
+func (l *List[T]) MoveAfter(e, mark *Element[T]) {
+       if e.list != l || e == mark || mark.list != l {
+               return
+       }
+       l.move(e, mark)
+}
+
+// PushBackList inserts a copy of an other list at the back of list l.
+// The lists l and other may be the same. They must not be nil.
+func (l *List[T]) PushBackList(other *List[T]) {
+       l.lazyInit()
+       for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
+               l.insertValue(e.Value, l.root.prev)
+       }
+}
+
+// PushFrontList inserts a copy of an other list at the front of list l.
+// The lists l and other may be the same. They must not be nil.
+func (l *List[T]) PushFrontList(other *List[T]) {
+       l.lazyInit()
+       for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
+               l.insertValue(e.Value, &l.root)
+       }
+}
+
+// Transform runs a transform function on a list returning a new list.
+func Transform[TElem1, TElem2 any](lst *List[TElem1], f func(TElem1) TElem2) *List[TElem2] {
+       ret := New[TElem2]()
+       for p := lst.Front(); p != nil; p = p.Next() {
+               ret.PushBack(f(p.Value))
+       }
+       return ret
+}
+
+func CheckListLen[T any](l *List[T], len int) bool {
+       if n := l.Len(); n != len {
+               panic(fmt.Sprintf("l.Len() = %d, want %d", n, len))
+               return false
+       }
+       return true
+}
+
+func CheckListPointers[T any](l *List[T], es []*Element[T]) {
+       root := &l.root
+
+       if !CheckListLen(l, len(es)) {
+               return
+       }
+
+       // zero length lists must be the zero value or properly initialized (sentinel circle)
+       if len(es) == 0 {
+               if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root {
+                       panic(fmt.Sprintf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root))
+               }
+               return
+       }
+       // len(es) > 0
+
+       // check internal and external prev/next connections
+       for i, e := range es {
+               prev := root
+               Prev := (*Element[T])(nil)
+               if i > 0 {
+                       prev = es[i-1]
+                       Prev = prev
+               }
+               if p := e.prev; p != prev {
+                       panic(fmt.Sprintf("elt[%d](%p).prev = %p, want %p", i, e, p, prev))
+               }
+               if p := e.Prev(); p != Prev {
+                       panic(fmt.Sprintf("elt[%d](%p).Prev() = %p, want %p", i, e, p, Prev))
+               }
+
+               next := root
+               Next := (*Element[T])(nil)
+               if i < len(es)-1 {
+                       next = es[i+1]
+                       Next = next
+               }
+               if n := e.next; n != next {
+                       panic(fmt.Sprintf("elt[%d](%p).next = %p, want %p", i, e, n, next))
+               }
+               if n := e.Next(); n != Next {
+                       panic(fmt.Sprintf("elt[%d](%p).Next() = %p, want %p", i, e, n, Next))
+               }
+       }
+}
diff --git a/test/typeparam/listimp2.dir/main.go b/test/typeparam/listimp2.dir/main.go
new file mode 100644 (file)
index 0000000..0c2c38e
--- /dev/null
@@ -0,0 +1,316 @@
+// 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 (
+       "a"
+       "fmt"
+       "strconv"
+)
+
+func TestList() {
+       l := a.New[string]()
+       a.CheckListPointers(l, []*(a.Element[string]){})
+
+       // Single element list
+       e := l.PushFront("a")
+       a.CheckListPointers(l, []*(a.Element[string]){e})
+       l.MoveToFront(e)
+       a.CheckListPointers(l, []*(a.Element[string]){e})
+       l.MoveToBack(e)
+       a.CheckListPointers(l, []*(a.Element[string]){e})
+       l.Remove(e)
+       a.CheckListPointers(l, []*(a.Element[string]){})
+
+       // Bigger list
+       l2 := a.New[int]()
+       e2 := l2.PushFront(2)
+       e1 := l2.PushFront(1)
+       e3 := l2.PushBack(3)
+       e4 := l2.PushBack(600)
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e2, e3, e4})
+
+       l2.Remove(e2)
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e3, e4})
+
+       l2.MoveToFront(e3) // move from middle
+       a.CheckListPointers(l2, []*(a.Element[int]){e3, e1, e4})
+
+       l2.MoveToFront(e1)
+       l2.MoveToBack(e3) // move from middle
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3})
+
+       l2.MoveToFront(e3) // move from back
+       a.CheckListPointers(l2, []*(a.Element[int]){e3, e1, e4})
+       l2.MoveToFront(e3) // should be no-op
+       a.CheckListPointers(l2, []*(a.Element[int]){e3, e1, e4})
+
+       l2.MoveToBack(e3) // move from front
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3})
+       l2.MoveToBack(e3) // should be no-op
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3})
+
+       e2 = l2.InsertBefore(2, e1) // insert before front
+       a.CheckListPointers(l2, []*(a.Element[int]){e2, e1, e4, e3})
+       l2.Remove(e2)
+       e2 = l2.InsertBefore(2, e4) // insert before middle
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e2, e4, e3})
+       l2.Remove(e2)
+       e2 = l2.InsertBefore(2, e3) // insert before back
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e2, e3})
+       l2.Remove(e2)
+
+       e2 = l2.InsertAfter(2, e1) // insert after front
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e2, e4, e3})
+       l2.Remove(e2)
+       e2 = l2.InsertAfter(2, e4) // insert after middle
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e2, e3})
+       l2.Remove(e2)
+       e2 = l2.InsertAfter(2, e3) // insert after back
+       a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3, e2})
+       l2.Remove(e2)
+
+       // Check standard iteration.
+       sum := 0
+       for e := l2.Front(); e != nil; e = e.Next() {
+               sum += e.Value
+       }
+       if sum != 604 {
+               panic(fmt.Sprintf("sum over l = %d, want 604", sum))
+       }
+
+       // Clear all elements by iterating
+       var next *a.Element[int]
+       for e := l2.Front(); e != nil; e = next {
+               next = e.Next()
+               l2.Remove(e)
+       }
+       a.CheckListPointers(l2, []*(a.Element[int]){})
+}
+
+func checkList[T comparable](l *a.List[T], es []interface{}) {
+       if !a.CheckListLen(l, len(es)) {
+               return
+       }
+
+       i := 0
+       for e := l.Front(); e != nil; e = e.Next() {
+               le := e.Value
+               // Comparison between a generically-typed variable le and an interface.
+               if le != es[i] {
+                       panic(fmt.Sprintf("elt[%d].Value = %v, want %v", i, le, es[i]))
+               }
+               i++
+       }
+}
+
+func TestExtending() {
+       l1 := a.New[int]()
+       l2 := a.New[int]()
+
+       l1.PushBack(1)
+       l1.PushBack(2)
+       l1.PushBack(3)
+
+       l2.PushBack(4)
+       l2.PushBack(5)
+
+       l3 := a.New[int]()
+       l3.PushBackList(l1)
+       checkList(l3, []interface{}{1, 2, 3})
+       l3.PushBackList(l2)
+       checkList(l3, []interface{}{1, 2, 3, 4, 5})
+
+       l3 = a.New[int]()
+       l3.PushFrontList(l2)
+       checkList(l3, []interface{}{4, 5})
+       l3.PushFrontList(l1)
+       checkList(l3, []interface{}{1, 2, 3, 4, 5})
+
+       checkList(l1, []interface{}{1, 2, 3})
+       checkList(l2, []interface{}{4, 5})
+
+       l3 = a.New[int]()
+       l3.PushBackList(l1)
+       checkList(l3, []interface{}{1, 2, 3})
+       l3.PushBackList(l3)
+       checkList(l3, []interface{}{1, 2, 3, 1, 2, 3})
+
+       l3 = a.New[int]()
+       l3.PushFrontList(l1)
+       checkList(l3, []interface{}{1, 2, 3})
+       l3.PushFrontList(l3)
+       checkList(l3, []interface{}{1, 2, 3, 1, 2, 3})
+
+       l3 = a.New[int]()
+       l1.PushBackList(l3)
+       checkList(l1, []interface{}{1, 2, 3})
+       l1.PushFrontList(l3)
+       checkList(l1, []interface{}{1, 2, 3})
+}
+
+func TestRemove() {
+       l := a.New[int]()
+       e1 := l.PushBack(1)
+       e2 := l.PushBack(2)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e2})
+       e := l.Front()
+       l.Remove(e)
+       a.CheckListPointers(l, []*(a.Element[int]){e2})
+       l.Remove(e)
+       a.CheckListPointers(l, []*(a.Element[int]){e2})
+}
+
+func TestIssue4103() {
+       l1 := a.New[int]()
+       l1.PushBack(1)
+       l1.PushBack(2)
+
+       l2 := a.New[int]()
+       l2.PushBack(3)
+       l2.PushBack(4)
+
+       e := l1.Front()
+       l2.Remove(e) // l2 should not change because e is not an element of l2
+       if n := l2.Len(); n != 2 {
+               panic(fmt.Sprintf("l2.Len() = %d, want 2", n))
+       }
+
+       l1.InsertBefore(8, e)
+       if n := l1.Len(); n != 3 {
+               panic(fmt.Sprintf("l1.Len() = %d, want 3", n))
+       }
+}
+
+func TestIssue6349() {
+       l := a.New[int]()
+       l.PushBack(1)
+       l.PushBack(2)
+
+       e := l.Front()
+       l.Remove(e)
+       if e.Value != 1 {
+               panic(fmt.Sprintf("e.value = %d, want 1", e.Value))
+       }
+       if e.Next() != nil {
+               panic(fmt.Sprintf("e.Next() != nil"))
+       }
+       if e.Prev() != nil {
+               panic(fmt.Sprintf("e.Prev() != nil"))
+       }
+}
+
+func TestMove() {
+       l := a.New[int]()
+       e1 := l.PushBack(1)
+       e2 := l.PushBack(2)
+       e3 := l.PushBack(3)
+       e4 := l.PushBack(4)
+
+       l.MoveAfter(e3, e3)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4})
+       l.MoveBefore(e2, e2)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4})
+
+       l.MoveAfter(e3, e2)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4})
+       l.MoveBefore(e2, e3)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4})
+
+       l.MoveBefore(e2, e4)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e3, e2, e4})
+       e2, e3 = e3, e2
+
+       l.MoveBefore(e4, e1)
+       a.CheckListPointers(l, []*(a.Element[int]){e4, e1, e2, e3})
+       e1, e2, e3, e4 = e4, e1, e2, e3
+
+       l.MoveAfter(e4, e1)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e4, e2, e3})
+       e2, e3, e4 = e4, e2, e3
+
+       l.MoveAfter(e2, e3)
+       a.CheckListPointers(l, []*(a.Element[int]){e1, e3, e2, e4})
+       e2, e3 = e3, e2
+}
+
+// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized a.List
+func TestZeroList() {
+       var l1 = new(a.List[int])
+       l1.PushFront(1)
+       checkList(l1, []interface{}{1})
+
+       var l2 = new(a.List[int])
+       l2.PushBack(1)
+       checkList(l2, []interface{}{1})
+
+       var l3 = new(a.List[int])
+       l3.PushFrontList(l1)
+       checkList(l3, []interface{}{1})
+
+       var l4 = new(a.List[int])
+       l4.PushBackList(l2)
+       checkList(l4, []interface{}{1})
+}
+
+// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
+func TestInsertBeforeUnknownMark() {
+       var l a.List[int]
+       l.PushBack(1)
+       l.PushBack(2)
+       l.PushBack(3)
+       l.InsertBefore(1, new(a.Element[int]))
+       checkList(&l, []interface{}{1, 2, 3})
+}
+
+// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
+func TestInsertAfterUnknownMark() {
+       var l a.List[int]
+       l.PushBack(1)
+       l.PushBack(2)
+       l.PushBack(3)
+       l.InsertAfter(1, new(a.Element[int]))
+       checkList(&l, []interface{}{1, 2, 3})
+}
+
+// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
+func TestMoveUnknownMark() {
+       var l1 a.List[int]
+       e1 := l1.PushBack(1)
+
+       var l2 a.List[int]
+       e2 := l2.PushBack(2)
+
+       l1.MoveAfter(e1, e2)
+       checkList(&l1, []interface{}{1})
+       checkList(&l2, []interface{}{2})
+
+       l1.MoveBefore(e1, e2)
+       checkList(&l1, []interface{}{1})
+       checkList(&l2, []interface{}{2})
+}
+
+// Test the Transform function.
+func TestTransform() {
+       l1 := a.New[int]()
+       l1.PushBack(1)
+       l1.PushBack(2)
+       l2 := a.Transform(l1, strconv.Itoa)
+       checkList(l2, []interface{}{"1", "2"})
+}
+
+
+func main() {
+       TestList()
+       TestExtending()
+       TestRemove()
+       TestIssue4103()
+       TestIssue6349()
+       TestMove()
+       TestZeroList()
+       TestInsertBeforeUnknownMark()
+       TestInsertAfterUnknownMark()
+       TestTransform()
+}
diff --git a/test/typeparam/listimp2.go b/test/typeparam/listimp2.go
new file mode 100644 (file)
index 0000000..76930e5
--- /dev/null
@@ -0,0 +1,7 @@
+// rundir -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 ignored