cmd/compile: clean up the switch statements in (*genInst).node()

There were two main outer switch statements in node() that can just be
combined. Also, for simplicity, changed an IsCmp() conditional into just
another case in the switch statement.

Also, the inner OCALL switch statement had a bunch of fairly duplicate
cases. Combined the cases that all had no special semantics, into a
single default case calling transformCall().

In the OCALL case in dictPass(), got rid of a check for OFUNCINST (which
will always have been removed by this point). Also, eliminated an assert
that could cause unneded failures. transformCall() should always be
called if the node op is still OCALL, so no need to assert on the ops of
call.X.

Added an extra test in issue47078.go, to explicitly check for case where
the X argument of a call is a DOTTYPE.

Change-Id: Ifb3f812ce12820a4ce08afe2887f00f7fc00cd2f
Reviewed-on: https://go-review.googlesource.com/c/go/+/358596
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>
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 68032e7..56010a3 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -928,8 +928,9 @@
 }
 
 // node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and
-// also descends into closures. It substitutes type arguments for type parameters
-// in all the new nodes.
+// also descends into closures. It substitutes type arguments for type parameters in
+// all the new nodes and does the transformations that were delayed on the generic
+// function.
 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
@@ -973,15 +974,15 @@
 		if _, isExpr := m.(ir.Expr); isExpr {
 			t := x.Type()
 			if t == nil {
-				// t can be nil only if this is a call that has no
-				// return values, so allow that and otherwise give
-				// an error.
+				// Check for known cases where t can be nil (call
+				// that has no return values, and key expressions)
+				// and otherwise cause a fatal error.
 				_, isCallExpr := m.(*ir.CallExpr)
 				_, isStructKeyExpr := m.(*ir.StructKeyExpr)
 				_, isKeyExpr := m.(*ir.KeyExpr)
 				if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC &&
 					x.Op() != ir.OCLOSE {
-					base.Fatalf(fmt.Sprintf("Nil type for %v", x))
+					base.FatalfAt(m.Pos(), "Nil type for %v", x)
 				}
 			} else if x.Op() != ir.OCLOSURE {
 				m.SetType(subst.ts.Typ(x.Type()))
@@ -991,56 +992,55 @@
 		ir.EditChildren(m, edit)
 
 		m.SetTypecheck(1)
-		if x.Op().IsCmp() {
-			transformCompare(m.(*ir.BinaryExpr))
-		} else {
-			switch x.Op() {
-			case ir.OSLICE, ir.OSLICE3:
-				transformSlice(m.(*ir.SliceExpr))
 
-			case ir.OADD:
-				m = transformAdd(m.(*ir.BinaryExpr))
-
-			case ir.OINDEX:
-				transformIndex(m.(*ir.IndexExpr))
-
-			case ir.OAS2:
-				as2 := m.(*ir.AssignListStmt)
-				transformAssign(as2, as2.Lhs, as2.Rhs)
-
-			case ir.OAS:
-				as := m.(*ir.AssignStmt)
-				if as.Y != nil {
-					// transformAssign doesn't handle the case
-					// of zeroing assignment of a dcl (rhs[0] is nil).
-					lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
-					transformAssign(as, lhs, rhs)
-					as.X, as.Y = lhs[0], rhs[0]
-				}
-
-			case ir.OASOP:
-				as := m.(*ir.AssignOpStmt)
-				transformCheckAssign(as, as.X)
-
-			case ir.ORETURN:
-				transformReturn(m.(*ir.ReturnStmt))
-
-			case ir.OSEND:
-				transformSend(m.(*ir.SendStmt))
-
-			case ir.OSELECT:
-				transformSelect(m.(*ir.SelectStmt))
-
-			case ir.OCOMPLIT:
-				transformCompLit(m.(*ir.CompLitExpr))
-
-			case ir.OADDR:
-				transformAddr(m.(*ir.AddrExpr))
-
-			}
-		}
-
+		// Do the transformations that we delayed on the generic function
+		// node, now that we have substituted in the type args.
 		switch x.Op() {
+		case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+			transformCompare(m.(*ir.BinaryExpr))
+
+		case ir.OSLICE, ir.OSLICE3:
+			transformSlice(m.(*ir.SliceExpr))
+
+		case ir.OADD:
+			m = transformAdd(m.(*ir.BinaryExpr))
+
+		case ir.OINDEX:
+			transformIndex(m.(*ir.IndexExpr))
+
+		case ir.OAS2:
+			as2 := m.(*ir.AssignListStmt)
+			transformAssign(as2, as2.Lhs, as2.Rhs)
+
+		case ir.OAS:
+			as := m.(*ir.AssignStmt)
+			if as.Y != nil {
+				// transformAssign doesn't handle the case
+				// of zeroing assignment of a dcl (rhs[0] is nil).
+				lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
+				transformAssign(as, lhs, rhs)
+				as.X, as.Y = lhs[0], rhs[0]
+			}
+
+		case ir.OASOP:
+			as := m.(*ir.AssignOpStmt)
+			transformCheckAssign(as, as.X)
+
+		case ir.ORETURN:
+			transformReturn(m.(*ir.ReturnStmt))
+
+		case ir.OSEND:
+			transformSend(m.(*ir.SendStmt))
+
+		case ir.OSELECT:
+			transformSelect(m.(*ir.SelectStmt))
+
+		case ir.OCOMPLIT:
+			transformCompLit(m.(*ir.CompLitExpr))
+
+		case ir.OADDR:
+			transformAddr(m.(*ir.AddrExpr))
+
 		case ir.OLITERAL:
 			t := m.Type()
 			if t != x.Type() {
@@ -1058,16 +1058,13 @@
 			}
 
 		case ir.OXDOT:
-			// A method value/call via a type param will have been
-			// left as an OXDOT. When we see this during stenciling,
-			// finish the transformation, now that we have the
-			// instantiated receiver type. We need to do this now,
-			// since the access/selection to the method for the real
-			// type is very different from the selection for the type
-			// param. m will be transformed to an OMETHVALUE node. It
-			// will be transformed to an ODOTMETH or ODOTINTER node if
-			// we find in the OCALL case below that the method value
-			// is actually called.
+			// Finish the transformation of an OXDOT, unless this was a
+			// bound call (a direct call on a type param). A bound call
+			// will be transformed during the dictPass. Otherwise, m
+			// will be transformed to an OMETHVALUE node. It will be
+			// transformed to an ODOTMETH or ODOTINTER node if we find in
+			// the OCALL case below that the method value is actually
+			// called.
 			mse := m.(*ir.SelectorExpr)
 			if src := mse.X.Type(); !src.IsShape() {
 				transformDot(mse, false)
@@ -1080,7 +1077,7 @@
 				// Transform the conversion, now that we know the
 				// type argument.
 				m = transformConvCall(call)
-				// CONVIFACE transformation was already done in node2
+				// CONVIFACE transformation was already done in noder2
 				assert(m.Op() != ir.OCONVIFACE)
 
 			case ir.OMETHVALUE, ir.OMETHEXPR:
@@ -1116,34 +1113,29 @@
 					transformCall(call)
 				}
 
-			case ir.OCLOSURE:
-				transformCall(call)
-
-			case ir.ODEREF, ir.OINDEX, ir.OINDEXMAP, ir.ORECV:
-				// Transform a call that was delayed because of the
-				// use of typeparam inside an expression that required
-				// a pointer dereference, array indexing, map indexing,
-				// or channel receive to compute function value.
-				transformCall(call)
-
-			case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
-				transformCall(call)
-
-			case ir.OCONVNOP:
-				transformCall(call)
-
 			case ir.OFUNCINST:
 				// A call with an OFUNCINST will get transformed
 				// in stencil() once we have created & attached the
 				// instantiation to be called.
 				// We must transform the arguments of the call now, though,
 				// so that any needed CONVIFACE nodes are exposed,
-				// so the dictionary format is correct
+				// so the dictionary format is correct.
 				transformEarlyCall(call)
 
-			case ir.OXDOT, ir.ODOTTYPE, ir.ODOTTYPE2:
+			case ir.OXDOT:
+				// This is the case of a bound call on a typeparam,
+				// which will be handled in the dictPass.
+
+			case ir.ODOTTYPE, ir.ODOTTYPE2:
+				// These are DOTTYPEs that could get transformed into
+				// ODYNAMIC DOTTYPEs by the dict pass.
+
 			default:
-				base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op()))
+				// Transform a call for all other values of
+				// call.X.Op() that don't require any special
+				// handling.
+				transformCall(call)
+
 			}
 
 		case ir.OCLOSURE:
@@ -1268,16 +1260,13 @@
 			}
 		case ir.OCALL:
 			op := m.(*ir.CallExpr).X.Op()
-			if op != ir.OFUNCINST {
-				assert(op == ir.OMETHVALUE || op == ir.OCLOSURE || op == ir.ODYNAMICDOTTYPE || op == ir.ODYNAMICDOTTYPE2)
-				if op == ir.OMETHVALUE {
-					// Redo the transformation of OXDOT, now that we
-					// know the method value is being called.
-					m.(*ir.CallExpr).X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
-					transformDot(m.(*ir.CallExpr).X.(*ir.SelectorExpr), true)
-				}
-				transformCall(m.(*ir.CallExpr))
+			if op == ir.OMETHVALUE {
+				// Redo the transformation of OXDOT, now that we
+				// know the method value is being called.
+				m.(*ir.CallExpr).X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
+				transformDot(m.(*ir.CallExpr).X.(*ir.SelectorExpr), true)
 			}
+			transformCall(m.(*ir.CallExpr))
 
 		case ir.OCONVIFACE:
 			if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
diff --git a/test/typeparam/issue47878.go b/test/typeparam/issue47878.go
index cb1043a..6ad183d 100644
--- a/test/typeparam/issue47878.go
+++ b/test/typeparam/issue47878.go
@@ -31,6 +31,13 @@
 	_ = (<-s)()
 }
 
+type Src5[T any] func() Src5[T]
+
+func (s Src5[T]) Next() {
+	var x interface{} = s
+	_ = (x.(Src5[T]))()
+}
+
 func main() {
 	var src1 Src1[int]
 	src1.Next()
@@ -43,4 +50,7 @@
 
 	var src4 Src4[int]
 	src4.Next()
+
+	var src5 Src5[int]
+	src5.Next()
 }