go/ssa: lift structs, arrays and typeparams

Lift locals with underlying types of struct, array and typeparam.

Changes to vta tests are due to the tests being sensitive to register names.

Change-Id: I558c0e6897b62f93ae1e700307e6e1e4dea008d0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/451996
Run-TryBot: Tim King <taking@google.com>
Commit-Queue: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/go/callgraph/vta/graph_test.go b/go/callgraph/vta/graph_test.go
index 8b8c697..da574d7 100644
--- a/go/callgraph/vta/graph_test.go
+++ b/go/callgraph/vta/graph_test.go
@@ -152,21 +152,21 @@
 	return vgs
 }
 
-// subGraph checks if a graph `g1` is a subgraph of graph `g2`.
-// Assumes that each element in `g1` and `g2` is an edge set
-// for a particular node in a fixed yet arbitrary format.
-func subGraph(g1, g2 []string) bool {
-	m := make(map[string]bool)
-	for _, s := range g2 {
-		m[s] = true
+// setdiff returns the set difference of `X-Y` or {s | s ∈ X, s ∉ Y }.
+func setdiff(X, Y []string) []string {
+	y := make(map[string]bool)
+	var delta []string
+	for _, s := range Y {
+		y[s] = true
 	}
 
-	for _, s := range g1 {
-		if _, ok := m[s]; !ok {
-			return false
+	for _, s := range X {
+		if _, ok := y[s]; !ok {
+			delta = append(delta, s)
 		}
 	}
-	return true
+	sort.Strings(delta)
+	return delta
 }
 
 func TestVTAGraphConstruction(t *testing.T) {
@@ -201,8 +201,9 @@
 			}
 
 			g, _ := typePropGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
-			if gs := vtaGraphStr(g); !subGraph(want, gs) {
-				t.Errorf("`%s`: want superset of %v;\n got %v", file, want, gs)
+			got := vtaGraphStr(g)
+			if diff := setdiff(want, got); len(diff) > 0 {
+				t.Errorf("`%s`: want superset of %v;\n got %v\ndiff: %v", file, want, got, diff)
 			}
 		})
 	}
diff --git a/go/callgraph/vta/testdata/src/callgraph_collections.go b/go/callgraph/vta/testdata/src/callgraph_collections.go
index bc418e3..6177a32 100644
--- a/go/callgraph/vta/testdata/src/callgraph_collections.go
+++ b/go/callgraph/vta/testdata/src/callgraph_collections.go
@@ -37,31 +37,5 @@
 	x[len(x)-1].Foo()
 }
 
-// Relevant SSA:
-// func Baz(a A, b B):
-//   ...
-//   t4 = Do(t2, t3)
-//   t5 = range t4
-//   jump 1
-//  1:
-//   t6 = phi [0: nil:[]I, 2: t16] #x
-//   t7 = next t5
-//   t8 = extract t7 #0
-//   if t8 goto 2 else 3
-//  2:
-//   t9 = extract t7 #1
-//   t10 = extract t7 #2
-//   t11 = invoke t9.Foo()
-//   t12 = invoke t10.Foo()
-//   ...
-//   jump 1
-//  3:
-//   t17 = len(t6)
-//   t18 = t17 - 1:int
-//   t19 = &t6[t18]
-//   t20 = *t19
-//   t21 = invoke t20.Foo()
-//   return
-
 // WANT:
-// Baz: Do(t2, t3) -> Do; invoke t10.Foo() -> B.Foo; invoke t20.Foo() -> A.Foo, B.Foo; invoke t9.Foo() -> A.Foo, B.Foo
+// Baz: Do(a, b) -> Do; invoke t16.Foo() -> A.Foo, B.Foo; invoke t5.Foo() -> A.Foo, B.Foo; invoke t6.Foo() -> B.Foo
diff --git a/go/callgraph/vta/testdata/src/callgraph_fields.go b/go/callgraph/vta/testdata/src/callgraph_fields.go
index 00aa649..9149bdd 100644
--- a/go/callgraph/vta/testdata/src/callgraph_fields.go
+++ b/go/callgraph/vta/testdata/src/callgraph_fields.go
@@ -31,61 +31,6 @@
 	a.Do()
 }
 
-// Relevant SSA:
-// func Baz(b B):
-//        t0 = local B (b)
-//        *t0 = b
-//        t1 = *t0
-//        t2 = NewA(t1)
-//        t3 = (*A).Do(t2)
-//        return
-//
-// func (a *A) Do():
-//        t0 = &a.I [#0]
-//        t1 = *t0
-//        t2 = invoke t1.Foo()
-//        return
-//
-// Name: (testdata.A).Foo
-// Synthetic: wrapper for func (testdata.I).Foo()
-// Location: testdata/callgraph_fields.go:10:2
-// func (arg0 testdata.A) Foo():
-//	  t0 = local testdata.A ()
-//        *t0 = arg0
-//        t1 = &t0.I [#0]
-//        t2 = *t1
-//        t3 = invoke t2.Foo()
-//        return
-//
-// Name: (*testdata.A).Foo
-// Synthetic: wrapper for func (testdata.I).Foo()
-// Location: testdata/callgraph_fields.go:10:2
-// func (arg0 *testdata.A) Foo():
-//        t0 = &arg0.I [#0]
-//        t1 = *t0
-//        t2 = invoke t1.Foo()
-//        return
-//
-// func (b B) Foo():
-//        t0 = local B (b)
-//        *t0 = b
-//        return
-//
-// func (b *testdata.B) Foo():
-//        t0 = ssa:wrapnilchk(b, "testdata.B":string, "Foo":string)
-//        t1 = *t0
-//        t2 = (testdata.B).Foo(t1)
-//        return
-//
-// func NewA(b B) *A:
-//        t0 = new B (b)
-//        *t0 = b
-//        t1 = new A (complit)
-//        t2 = &t1.I [#0]
-//        t3 = make I <- *B (t0)
-//        *t2 = t3
-//        return t1
-
 // WANT:
-// Baz: (*A).Do(t2) -> A.Do; NewA(t1) -> NewA
+// Baz: (*A).Do(t0) -> A.Do; NewA(b) -> NewA
 // A.Do: invoke t1.Foo() -> B.Foo
diff --git a/go/callgraph/vta/testdata/src/callgraph_generics.go b/go/callgraph/vta/testdata/src/callgraph_generics.go
index da3dca5..a0ac8fd 100644
--- a/go/callgraph/vta/testdata/src/callgraph_generics.go
+++ b/go/callgraph/vta/testdata/src/callgraph_generics.go
@@ -39,33 +39,24 @@
 
 // Relevant SSA:
 //func Foo(a A, b B):
-//  t0 = local A (a)
-//  *t0 = a
-//  t1 = local B (b)
-//  *t1 = b
-//  t2 = new bool (x)
-//  *t2 = true:bool
-//  t3 = instantiated[bool](t2)
-//  t4 = new int (y)
-//  *t4 = 1:int
-//  t5 = instantiated[int](t4)
-//  t6 = *t0
-//  t7 = interfaceInstantiated[testdata.A](t6)
-//  t8 = *t1
-//  t9 = interfaceInstantiated[testdata.B](t8)
+//  t0 = new bool (x)
+//  *t0 = true:bool
+//  t1 = instantiated[bool](t2)
+//  t1 = new int (y)
+//  *t2 = 1:int
+//  t3 = instantiated[[int]](t4)
+//  t4 = interfaceInstantiated[testdata.A](a)
+//  t5 = interfaceInstantiated[testdata.B](b)
 //  return
 //
-//func interfaceInstantiated[testdata.B](x B):
-//  t0 = local B (x)
-//  *t0 = x
-//  t1 = *t0
-//  t2 = (B).Bar(t1)
+//func interfaceInstantiated[[testdata.B]](x B):
+//  t0 = (B).Bar(b)
 //  return
 //
 //func interfaceInstantiated[X I](x X):
 //        (external)
 
 // WANT:
-// Foo: instantiated[bool](t2) -> instantiated[bool]; instantiated[int](t4) -> instantiated[int]; interfaceInstantiated[testdata.A](t6) -> interfaceInstantiated[testdata.A]; interfaceInstantiated[testdata.B](t8) -> interfaceInstantiated[testdata.B]
-// interfaceInstantiated[testdata.B]: (B).Bar(t1) -> B.Bar
-// interfaceInstantiated[testdata.A]: (A).Bar(t1) -> A.Bar
+// Foo: instantiated[bool](t0) -> instantiated[bool]; instantiated[int](t2) -> instantiated[int]; interfaceInstantiated[testdata.A](a) -> interfaceInstantiated[testdata.A]; interfaceInstantiated[testdata.B](b) -> interfaceInstantiated[testdata.B]
+// interfaceInstantiated[testdata.B]: (B).Bar(x) -> B.Bar
+// interfaceInstantiated[testdata.A]: (A).Bar(x) -> A.Bar
diff --git a/go/callgraph/vta/testdata/src/callgraph_interfaces.go b/go/callgraph/vta/testdata/src/callgraph_interfaces.go
index 123468c..c272ff0 100644
--- a/go/callgraph/vta/testdata/src/callgraph_interfaces.go
+++ b/go/callgraph/vta/testdata/src/callgraph_interfaces.go
@@ -49,13 +49,11 @@
 
 // func Do(b bool) I:
 //    ...
-//   t3 = local C (c)
-//   t4 = *t3
-//   t5 = (C).Foo(t4)
-//   t6 = NewB()
-//   t7 = make I <- B (t6)
-//   return t7
+//   t1 = (C).Foo(struct{}{}:C)
+//   t2 = NewB()
+//   t3 = make I <- B (t2)
+//   return t3
 
 // WANT:
 // Baz: Do(b) -> Do; invoke t0.Foo() -> A.Foo, B.Foo
-// Do: (C).Foo(t4) -> C.Foo; NewB() -> NewB
+// Do: (C).Foo(struct{}{}:C) -> C.Foo; NewB() -> NewB
diff --git a/go/callgraph/vta/testdata/src/callgraph_pointers.go b/go/callgraph/vta/testdata/src/callgraph_pointers.go
index e07f969..ae8fe43 100644
--- a/go/callgraph/vta/testdata/src/callgraph_pointers.go
+++ b/go/callgraph/vta/testdata/src/callgraph_pointers.go
@@ -35,37 +35,9 @@
 	(*x).Foo()
 }
 
-// Relevant SSA:
-// func Baz(a A, b B, c bool):
-//   t0 = local A (a)
-//   ...
-//   t5 = Do(t2, t4, c)
-//   t6 = *t5
-//   t7 = invoke t6.Foo()
-//   return
-
-// func Do(a A, i I, c bool) *I:
-//   t0 = local A (a)
-//   *t0 = a
-//   ...
-//   if c goto 1 else 3
-//  1:
-//   t2 = &t0.f [#0]
-//   ...
-//   jump 2
-//  2:
-//   t6 = &t0.f [#0]
-//   ...
-//   t9 = invoke t8.Foo()
-//   return t1
-//  3:
-//   t10 = &t0.f [#0]      alias between A.f and t10
-//   *t10 = t1             alias between t10 and t1
-//   jump 2
-
 // The command a.f = &i introduces aliasing that results in
 // A and B reaching both *A.f and return value of Do(a, b, c).
 
 // WANT:
-// Baz: Do(t2, t4, c) -> Do; invoke t6.Foo() -> A.Foo, B.Foo
+// Baz: Do(a, t0, c) -> Do; invoke t2.Foo() -> A.Foo, B.Foo
 // Do: invoke t8.Foo() -> A.Foo, B.Foo
diff --git a/go/callgraph/vta/testdata/src/callgraph_static.go b/go/callgraph/vta/testdata/src/callgraph_static.go
index 1ed904f..147ed46 100644
--- a/go/callgraph/vta/testdata/src/callgraph_static.go
+++ b/go/callgraph/vta/testdata/src/callgraph_static.go
@@ -20,11 +20,9 @@
 
 // Relevant SSA:
 // func Baz(a A):
-//   ...
-//   t2 = (A).foo(t1)
-//   t3 = Bar()
-//   ...
-//   t6 = Baz(t5)
+//   t0 = (A).foo(a)
+//   t1 = Bar()
+//   t2 = Baz(struct{}{}:A)
 
 // WANT:
-// Baz: (A).foo(t1) -> A.foo; Bar() -> Bar; Baz(t5) -> Baz
+// Baz: (A).foo(a) -> A.foo; Bar() -> Bar; Baz(struct{}{}:A) -> Baz
diff --git a/go/callgraph/vta/testdata/src/dynamic_calls.go b/go/callgraph/vta/testdata/src/dynamic_calls.go
index b8c14b2..f8f8898 100644
--- a/go/callgraph/vta/testdata/src/dynamic_calls.go
+++ b/go/callgraph/vta/testdata/src/dynamic_calls.go
@@ -27,23 +27,22 @@
 	return h()
 }
 
+var g *B = &B{} // ensure *B.foo is created.
+
 // Relevant SSA:
 // func Baz(x B, h func() I, i I) I:
-//   t0 = local B (x)
-//   *t0 = x
-//   t1 = *t0
-//   t2 = make I <- B (t1)
-//   t3 = invoke i.foo(t2)
-//   t4 = h()
-//   return t4
+//   t0 = make I <- B (x)
+//   t1 = invoke i.foo(t0)
+//   t2 = h()
+//   return t2
 
-// Local(t2) has seemingly duplicates of successors. This
+// Local(t0) has seemingly duplicates of successors. This
 // happens in stringification of type propagation graph.
 // Due to CHA, we analyze A.foo and *A.foo as well as B.foo
 // and *B.foo, which have similar bodies and hence similar
 // type flow that gets merged together during stringification.
 
 // WANT:
-// Local(t2) -> Local(ai), Local(ai), Local(bi), Local(bi)
-// Constant(testdata.I) -> Local(t4)
-// Local(t1) -> Local(t2)
+// Local(t0) -> Local(ai), Local(ai), Local(bi), Local(bi)
+// Constant(testdata.I) -> Local(t2)
+// Local(x) -> Local(t0)
diff --git a/go/callgraph/vta/testdata/src/maps.go b/go/callgraph/vta/testdata/src/maps.go
index b7354dc..f5f51a3 100644
--- a/go/callgraph/vta/testdata/src/maps.go
+++ b/go/callgraph/vta/testdata/src/maps.go
@@ -30,23 +30,16 @@
 
 // Relevant SSA:
 // func Baz(m map[I]I, b1 B, b2 B, n map[string]*J) *J:
-//   t0 = local B (b1)
-//   *t0 = b1
-//   t1 = local B (b2)
-//   *t1 = b2
-//   t2 = *t0
-//   t3 = make I <- B (t2)
-//   t4 = *t1
-//   t5 = make I <- B (t4)
-//   m[t3] = t5
-//   t6 = *t0
-//   t7 = (B).Foo(t6)
-//   t8 = n[t7]
-//   return t8
+//   t0 = make I <- B (b1)
+//   t1 = make I <- B (b2)
+//   m[t0] = t1
+//   t2 = (B).Foo(b1)
+//   t3 = n[t2]
+//   return t3
 
 // WANT:
-// Local(t4) -> Local(t5)
-// Local(t5) -> MapValue(testdata.I)
-// Local(t3) -> MapKey(testdata.I)
-// Local(t8) -> MapValue(*testdata.J)
-// MapValue(*testdata.J) -> Local(t8)
+// Local(b2) -> Local(t1)
+// Local(t1) -> MapValue(testdata.I)
+// Local(t0) -> MapKey(testdata.I)
+// Local(t3) -> MapValue(*testdata.J)
+// MapValue(*testdata.J) -> Local(t3)
diff --git a/go/callgraph/vta/testdata/src/panic.go b/go/callgraph/vta/testdata/src/panic.go
index 5ef3548..1a987d0 100644
--- a/go/callgraph/vta/testdata/src/panic.go
+++ b/go/callgraph/vta/testdata/src/panic.go
@@ -50,18 +50,14 @@
 //   return
 //
 // func Baz(i I):
-//   t0 = local A (a)
-//   *t0 = a
 //   defer recover1()
-//   defer recover()
-//   t1 = *t0
-//   t2 = make interface{} <- A (t1)
+//   t0 = make interface{} <- A (a)
 //   panic t2
 
-// t2 argument to panic in Baz gets ultimately connected to recover
+// t0 argument to panic in Baz gets ultimately connected to recover
 // registers t1 in recover1() and t0 in recover2().
 
 // WANT:
 // Panic -> Recover
-// Local(t2) -> Panic
+// Local(t0) -> Panic
 // Recover -> Local(t0), Local(t1)
diff --git a/go/callgraph/vta/testdata/src/phi.go b/go/callgraph/vta/testdata/src/phi.go
index 2144a2c..a4a3efd 100644
--- a/go/callgraph/vta/testdata/src/phi.go
+++ b/go/callgraph/vta/testdata/src/phi.go
@@ -28,28 +28,23 @@
 // Relevant SSA:
 // func Baz(b B, c bool):
 // 0:
-//  t0 = local B (b)
-//  *t0 = b
 //  if c goto 1 else 3
 //
 // 1:
-//  t1 = *t0
-//  t2 = make I <- B (t1)
+//  t0 = make I <- B (b)
 //  jump 2
 //
 // 2:
-//  t3 = phi [1: t2, 3: t7] #i
-//  t4 = invoke t3.foo()
+//  t1 = phi [1: t0, 3: t3] #i
+//  t2 = invoke t1.foo()
 //  return
 //
 // 3:
-//  t5 = local A (a)
-//  t6 = *t5
-//  t7 = make I <- A (t6)
+//  t3 = make I <- A (struct{}{}:A)
 //  jump 2
 
 // WANT:
-// Local(t1) -> Local(t2)
-// Local(t2) -> Local(t3)
-// Local(t7) -> Local(t3)
-// Local(t6) -> Local(t7)
+// Local(b) -> Local(t0)
+// Local(t0) -> Local(t1)
+// Local(t3) -> Local(t1)
+// Constant(testdata.A) -> Local(t3)
diff --git a/go/callgraph/vta/testdata/src/select.go b/go/callgraph/vta/testdata/src/select.go
index 50586d3..8774aec 100644
--- a/go/callgraph/vta/testdata/src/select.go
+++ b/go/callgraph/vta/testdata/src/select.go
@@ -39,22 +39,20 @@
 // Relevant SSA:
 // func Baz(b1 B, b2 B, c1 chan I, c2 chan J):
 //   ...
-//   t2 = *t0
-//   t3 = make I <- B (t2)
-//   t4 = *t1
-//   t5 = make J <- B (t4)
-//   t6 = select blocking [c1<-t3, c2<-t5, <-c1, <-c2] (index int, ok bool, I, J)
-//   t7 = extract t6 #0
-//   t8 = t7 == 0:int
-//   if t8 goto 2 else 3
+//   t0 = make I <- B (b1)
+//   t1 = make J <- B (b2)
+//   t2 = select blocking [c1<-t0, c2<-t1, <-c1, <-c2] (index int, ok bool, I, J)
+//   t3 = extract t2 #0
+//   t4 = t73== 0:int
+//   if t4 goto 2 else 3
 //         ...
 //  8:
-//   t15 = extract t6 #3
-//   t16 = invoke t15.Foo()
-//   t17 = print(t18)
+//   t12 = extract t2 #3
+//   t13 = invoke t12.Foo()
+//   t14 = print(t15)
 
 // WANT:
-// Local(t3) -> Channel(chan testdata.I)
-// Local(t5) -> Channel(chan testdata.J)
-// Channel(chan testdata.I) -> Local(t6[2])
-// Channel(chan testdata.J) -> Local(t6[3])
+// Local(t0) -> Channel(chan testdata.I)
+// Local(t1) -> Channel(chan testdata.J)
+// Channel(chan testdata.I) -> Local(t2[2])
+// Channel(chan testdata.J) -> Local(t2[3])
diff --git a/go/callgraph/vta/testdata/src/store.go b/go/callgraph/vta/testdata/src/store.go
index 8a36d4e..322e9e5 100644
--- a/go/callgraph/vta/testdata/src/store.go
+++ b/go/callgraph/vta/testdata/src/store.go
@@ -26,16 +26,14 @@
 }
 
 // Relevant SSA:
-//	t0 = local A (a)
-//	t1 = new I (i)
-//	t2 = *t0                 no interesting flow: concrete types
-//	t3 = make I <- A (t2)    t2 -> t3
-//	*t1 = t3                 t3 -> t1
-//	t4 = *t1                 t1 -> t4
-//	t5 = invoke t4.foo()
+//	t0 = new I (i)
+//	t1 = make I <- A (struct{}{}:A)    A  -> t1
+//	*t0 = t1                           t1 -> t0
+//	t2 = *t0                           t0 -> t2
+//	t3 = invoke t2.foo()
 //	return
 
 // WANT:
-// Local(t2) -> Local(t3)
-// Local(t3) -> Local(t1)
-// Local(t1) -> Local(t4)
+// Constant(testdata.A) -> Local(t1)
+// Local(t1) -> Local(t0)
+// Local(t0) -> Local(t2)
diff --git a/go/callgraph/vta/testdata/src/store_load_alias.go b/go/callgraph/vta/testdata/src/store_load_alias.go
index b9814f0..71f843b 100644
--- a/go/callgraph/vta/testdata/src/store_load_alias.go
+++ b/go/callgraph/vta/testdata/src/store_load_alias.go
@@ -27,26 +27,24 @@
 //   t1 = new *I (j)
 //   *t1 = t0
 //   t2 = *t1
-//   t3 = local A (complit)
-//   t4 = *t3
-//   t5 = make I <- A (t4)
-//   *t2 = t5
-//   t6 = *t0
-//   t7 = invoke t6.foo()
-//   t8 = *t1
-//   t9 = *t8
-//   t10 = invoke t9.foo()
+// 	 t3 = make I <- A (struct{}{}:A)                                       I
+//   *t2 = t3
+//   t4 = *t0
+//   t5 = invoke t4.foo()
+//   t6 = *t1
+//   t7 = *t6
+//   t8 = invoke t7.foo()
 
 // Flow chain showing that A reaches i.foo():
-//   t4 (A) -> t5 -> t2 <-> PtrInterface(I) <-> t0 -> t6
+//   Constant(A) -> t3 -> t2 <-> PtrInterface(I) <-> t0 -> t4
 // Flow chain showing that A reaches (**k).foo():
-//	 t4 (A) -> t5 -> t2 <-> PtrInterface(I) <-> t8 -> t9
+//	 Constant(A) -> t3 -> t2 <-> PtrInterface(I) <-> t6 -> t7
 
 // WANT:
 // Local(i) -> Local(t0)
-// Local(t0) -> Local(t6), PtrInterface(testdata.I)
-// PtrInterface(testdata.I) -> Local(t0), Local(t2), Local(t8)
+// Local(t0) -> Local(t4), PtrInterface(testdata.I)
+// PtrInterface(testdata.I) -> Local(t0), Local(t2), Local(t6)
 // Local(t2) -> PtrInterface(testdata.I)
-// Local(t4) -> Local(t5)
-// Local(t5) -> Local(t2)
-// Local(t8) -> Local(t9), PtrInterface(testdata.I)
+// Constant(testdata.A) -> Local(t3)
+// Local(t3) -> Local(t2)
+// Local(t6) -> Local(t7), PtrInterface(testdata.I)
diff --git a/go/callgraph/vta/testdata/src/type_conversions.go b/go/callgraph/vta/testdata/src/type_conversions.go
index e18077f..3dbfb0c 100644
--- a/go/callgraph/vta/testdata/src/type_conversions.go
+++ b/go/callgraph/vta/testdata/src/type_conversions.go
@@ -60,26 +60,22 @@
 //   t5 = change interface X <- Y (t4)
 //   t6 = invoke t5.Foo()
 //
-//   t7 = local A (complit)
-//   t8 = *t7
-//   t9 = make Y <- A (t8)
-//   *t0 = t9
-//   t10 = changetype *W <- *Y (t0)
-//   t11 = local B (complit)
-//   t12 = *t11
-//   t13 = make W <- B (t12)
-//   *t10 = t13
-//   t14 = *t0
-//   t15 = invoke t14.Foo()
-//   t16 = *t10
-//   t17 = invoke t16.Foo()
+//   t7 = make Y <- A (struct{}{}:A)
+//   *t0 = t7
+//   t8 = changetype *W <- *Y (t0)
+//   t9 = make W <- B (struct{}{}:B)
+//   *t8 = t9
+//   t10 = *t0
+//   t11 = invoke t10.Foo()
+//   t12 = *t8
+//   t13 = invoke t12.Foo()
 //   return
 
 // WANT:
 // Local(t1) -> Local(t2)
 // Local(t4) -> Local(t5)
-// Local(t0) -> Local(t1), Local(t10), Local(t14), Local(t4)
+// Local(t0) -> Local(t1), Local(t10), Local(t4), Local(t8)
 // Local(y) -> Local(t0)
-// Local(t8) -> Local(t9)
-// Local(t9) -> Local(t0)
-// Local(t13) -> Local(t10)
+// Constant(testdata.A) -> Local(t7)
+// Local(t7) -> Local(t0)
+// Local(t9) -> Local(t8)
diff --git a/go/callgraph/vta/vta_go117_test.go b/go/callgraph/vta/vta_go117_test.go
index 04f6980..6a5af2c 100644
--- a/go/callgraph/vta/vta_go117_test.go
+++ b/go/callgraph/vta/vta_go117_test.go
@@ -26,7 +26,8 @@
 	}
 
 	g, _ := typePropGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
-	if gs := vtaGraphStr(g); !subGraph(want, gs) {
-		t.Errorf("`%s`: want superset of %v;\n got %v", file, want, gs)
+	got := vtaGraphStr(g)
+	if diff := setdiff(want, got); len(diff) != 0 {
+		t.Errorf("`%s`: want superset of %v;\n got %v", file, want, got)
 	}
 }
diff --git a/go/callgraph/vta/vta_test.go b/go/callgraph/vta/vta_test.go
index 549c4af..75a8ceb 100644
--- a/go/callgraph/vta/vta_test.go
+++ b/go/callgraph/vta/vta_test.go
@@ -38,8 +38,9 @@
 			}
 
 			g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
-			if got := callGraphStr(g); !subGraph(want, got) {
-				t.Errorf("computed callgraph %v should contain %v", got, want)
+			got := callGraphStr(g)
+			if diff := setdiff(want, got); len(diff) > 0 {
+				t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff)
 			}
 		})
 	}
@@ -61,8 +62,9 @@
 	g := CallGraph(allFuncs, cha.CallGraph(prog))
 	// VTA over the whole program will produce a call graph that
 	// includes Baz:(**i).Foo -> A.Foo, B.Foo.
-	if got := callGraphStr(g); !subGraph(want, got) {
-		t.Errorf("computed callgraph %v should contain %v", got, want)
+	got := callGraphStr(g)
+	if diff := setdiff(want, got); len(diff) > 0 {
+		t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff)
 	}
 
 	// Prune the set of program functions to exclude Bar(). This should
@@ -78,8 +80,9 @@
 	}
 	want = []string{"Baz: Do(i) -> Do; invoke t2.Foo() -> A.Foo"}
 	g = CallGraph(noBarFuncs, cha.CallGraph(prog))
-	if got := callGraphStr(g); !subGraph(want, got) {
-		t.Errorf("pruned callgraph %v should contain %v", got, want)
+	got = callGraphStr(g)
+	if diff := setdiff(want, got); len(diff) > 0 {
+		t.Errorf("pruned callgraph %v should contain %v (diff: %v)", got, want, diff)
 	}
 }
 
@@ -131,7 +134,8 @@
 	}
 
 	g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
-	if got := callGraphStr(g); !subGraph(want, got) {
-		t.Errorf("computed callgraph %v should contain %v", got, want)
+	got := callGraphStr(g)
+	if diff := setdiff(want, got); len(diff) != 0 {
+		t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff)
 	}
 }
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index be8d36a..ffa6667 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -1282,8 +1282,7 @@
 	case *types.Struct:
 		if !isZero && len(e.Elts) != t.NumFields() {
 			// memclear
-			sb.store(&address{addr, e.Lbrace, nil},
-				zeroValue(fn, deref(addr.Type())))
+			sb.store(&address{addr, e.Lbrace, nil}, zeroConst(deref(addr.Type())))
 			isZero = true
 		}
 		for i, e := range e.Elts {
@@ -1327,8 +1326,7 @@
 
 			if !isZero && int64(len(e.Elts)) != at.Len() {
 				// memclear
-				sb.store(&address{array, e.Lbrace, nil},
-					zeroValue(fn, deref(array.Type())))
+				sb.store(&address{array, e.Lbrace, nil}, zeroConst(deref(array.Type())))
 			}
 		}
 
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index 1731c79..4ba049d 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -537,15 +537,46 @@
 	return v
 }
 
-// zeroValue emits to f code to produce a zero value of type t,
-// and returns it.
-func zeroValue(f *Function, t types.Type) Value {
-	switch t.Underlying().(type) {
-	case *types.Struct, *types.Array:
-		return emitLoad(f, f.addLocal(t, token.NoPos))
-	default:
-		return zeroConst(t)
-	}
+// emitSliceToArray emits to f code to convert a slice value to an array value.
+//
+// Precondition: all types in type set of typ are arrays and convertible to all
+// types in the type set of val.Type().
+func emitSliceToArray(f *Function, val Value, typ types.Type) Value {
+	// Emit the following:
+	// if val == nil && len(typ) == 0 {
+	//    ptr = &[0]T{}
+	// } else {
+	//	  ptr = SliceToArrayPointer(val)
+	// }
+	// v = *ptr
+
+	ptype := types.NewPointer(typ)
+	p := &SliceToArrayPointer{X: val}
+	p.setType(ptype)
+	ptr := f.emit(p)
+
+	nilb := f.newBasicBlock("slicetoarray.nil")
+	nonnilb := f.newBasicBlock("slicetoarray.nonnil")
+	done := f.newBasicBlock("slicetoarray.done")
+
+	cond := emitCompare(f, token.EQL, ptr, zeroConst(ptype), token.NoPos)
+	emitIf(f, cond, nilb, nonnilb)
+	f.currentBlock = nilb
+
+	zero := f.addLocal(typ, token.NoPos)
+	emitJump(f, done)
+	f.currentBlock = nonnilb
+
+	emitJump(f, done)
+	f.currentBlock = done
+
+	phi := &Phi{Edges: []Value{zero, ptr}, Comment: "slicetoarray"}
+	phi.pos = val.Pos()
+	phi.setType(typ)
+	x := f.emit(phi)
+	unOp := &UnOp{Op: token.MUL, X: x}
+	unOp.setType(typ)
+	return f.emit(unOp)
 }
 
 // createRecoverBlock emits to f a block of code to return after a
@@ -577,7 +608,7 @@
 			T := R.At(i).Type()
 
 			// Return zero value of each result type.
-			results = append(results, zeroValue(f, T))
+			results = append(results, zeroConst(T))
 		}
 	}
 	f.emit(&Return{Results: results})
diff --git a/go/ssa/lift.go b/go/ssa/lift.go
index 945536b..b9cf7bc 100644
--- a/go/ssa/lift.go
+++ b/go/ssa/lift.go
@@ -41,11 +41,8 @@
 import (
 	"fmt"
 	"go/token"
-	"go/types"
 	"math/big"
 	"os"
-
-	"golang.org/x/tools/internal/typeparams"
 )
 
 // If true, show diagnostic information at each step of lifting.
@@ -383,12 +380,6 @@
 //
 // fresh is a source of fresh ids for phi nodes.
 func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool {
-	// TODO(taking): zero constants of aggregated types can now be lifted.
-	switch deref(alloc.Type()).Underlying().(type) {
-	case *types.Array, *types.Struct, *typeparams.TypeParam:
-		return false
-	}
-
 	// Don't lift named return values in functions that defer
 	// calls that may recover from panic.
 	if fn := alloc.Parent(); fn.Recover != nil {
diff --git a/go/ssa/testdata/valueforexpr.go b/go/ssa/testdata/valueforexpr.go
index 243ec61..703c316 100644
--- a/go/ssa/testdata/valueforexpr.go
+++ b/go/ssa/testdata/valueforexpr.go
@@ -94,18 +94,18 @@
 	_ = & /*@Slice*/ ([]int{})
 
 	// 2. Arrays
-	print( /*@UnOp*/ ([1]int{}))
+	print( /*@Const*/ ([1]int{}))
 	print( /*@Alloc*/ (&[1]int{}))
 	print(& /*@Alloc*/ ([1]int{}))
 
-	arr1 := /*@Alloc*/ ([1]int{})
+	arr1 := /*@Const*/ ([1]int{})
 	arr2 := /*@Alloc*/ (&[1]int{})
 	arr3 := & /*@Alloc*/ ([1]int{})
 	_, _, _ = arr1, arr2, arr3
 
-	_ = /*@UnOp*/ ([1]int{})
-	_ = /*@Alloc*/ (& /*@Alloc*/ ([1]int{}))
-	_ = & /*@Alloc*/ ([1]int{})
+	_ = /*@Const*/ ([1]int{})
+	_ = /*@nil*/ (& /*@Const*/ ([1]int{})) // & optimized away
+	_ = & /*@Const*/ ([1]int{})
 
 	// 3. Maps
 	type M map[int]int
@@ -123,18 +123,18 @@
 	_ = & /*@MakeMap*/ (M{})
 
 	// 4. Structs
-	print( /*@UnOp*/ (struct{}{}))
+	print( /*@Const*/ (struct{}{}))
 	print( /*@Alloc*/ (&struct{}{}))
 	print(& /*@Alloc*/ (struct{}{}))
 
-	s1 := /*@Alloc*/ (struct{}{})
+	s1 := /*@Const*/ (struct{}{})
 	s2 := /*@Alloc*/ (&struct{}{})
 	s3 := & /*@Alloc*/ (struct{}{})
 	_, _, _ = s1, s2, s3
 
-	_ = /*@UnOp*/ (struct{}{})
-	_ = /*@Alloc*/ (& /*@Alloc*/ (struct{}{}))
-	_ = & /*@Alloc*/ (struct{}{})
+	_ = /*@Const*/ (struct{}{})
+	_ = /*@nil*/ (& /*@Const*/ (struct{}{})) // & optimized away
+	_ = & /*@Const*/ (struct{}{})
 }
 
 type t struct{ x int }