[dev.typeparams] cmd/compile: more incremental typecheck for unified IR

CL 332469 changed the unified IR reader to incrementally typecheck
each statement as they're read/constructed. This CL goes further to
incrementally typecheck each expression.

While here, this CL reorganizes a few things to make this go more
smoothly. In particular, it renames expr to expr0 and adds a new expr
wrapper that applies typecheck.Expr; gets rid of exprTypeSwitchguard
by moving that logic into switchStmt; and splits exprConvert out from
exprCall, which simplifies the logic for typechecking the calleee
expression somewhat.

Change-Id: I6289de9388dc94a947971f4b7213aafeb2faa5dc
Reviewed-on: https://go-review.googlesource.com/c/go/+/333730
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go
index 4a6a4e8..f8cb772 100644
--- a/src/cmd/compile/internal/noder/codes.go
+++ b/src/cmd/compile/internal/noder/codes.go
@@ -107,9 +107,7 @@
 	exprUnaryOp
 	exprBinaryOp
 	exprCall
-
-	// TODO(mdempsky): Handle in switchStmt directly instead.
-	exprTypeSwitchGuard
+	exprConvert
 )
 
 type codeDecl int
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 122bc70..19e51d9 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -1252,7 +1252,7 @@
 			continue
 		}
 
-		lhs[i] = r.expr()
+		lhs[i] = typecheck.AssignExpr(r.expr0())
 	}
 
 	return names, lhs
@@ -1351,7 +1351,21 @@
 	r.openScope()
 	pos := r.pos()
 	init := r.stmt()
-	tag := r.expr()
+
+	var tag ir.Node
+	if r.bool() {
+		pos := r.pos()
+		var ident *ir.Ident
+		if r.bool() {
+			pos := r.pos()
+			sym := typecheck.Lookup(r.string())
+			ident = ir.NewIdent(pos, sym)
+		}
+		x := r.expr()
+		tag = ir.NewTypeSwitchGuard(pos, ident, x)
+	} else {
+		tag = r.expr()
+	}
 
 	tswitch, ok := tag.(*ir.TypeSwitchGuard)
 	if ok && tswitch.Tag == nil {
@@ -1432,7 +1446,19 @@
 
 // @@@ Expressions
 
+// expr reads and returns a typechecked expression.
 func (r *reader) expr() ir.Node {
+	n := r.expr0()
+	if n == nil || n.Op() == ir.OTYPE {
+		// TODO(mdempsky): Push this responsibility up to callers?
+		return n
+	}
+	return typecheck.Expr(n)
+}
+
+// expr0 reads and returns an expression, possibly untypechecked.
+// The caller must typecheck the result as appropriate for its context.
+func (r *reader) expr0() ir.Node {
 	switch tag := codeExpr(r.code(syncExpr)); tag {
 	default:
 		panic("unhandled expression")
@@ -1522,22 +1548,17 @@
 		return ir.NewBinaryExpr(pos, op, x, y)
 
 	case exprCall:
-		fun := r.expr()
+		fun := typecheck.Callee(r.expr0())
 		pos := r.pos()
 		args := r.exprs()
 		dots := r.bool()
 		return typecheck.Call(pos, fun, args, dots)
 
-	case exprTypeSwitchGuard:
+	case exprConvert:
+		typ := r.typ()
 		pos := r.pos()
-		var tag *ir.Ident
-		if r.bool() {
-			pos := r.pos()
-			sym := typecheck.Lookup(r.string())
-			tag = ir.NewIdent(pos, sym)
-		}
 		x := r.expr()
-		return ir.NewTypeSwitchGuard(pos, tag, x)
+		return ir.NewConvExpr(pos, ir.OCONV, typ, x)
 	}
 }
 
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index 6348a56..3f93105 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -1033,7 +1033,17 @@
 	w.openScope(stmt.Pos())
 	w.pos(stmt)
 	w.stmt(stmt.Init)
-	w.expr(stmt.Tag)
+
+	if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.bool(ok) {
+		w.pos(guard)
+		if tag := guard.Lhs; w.bool(tag != nil) {
+			w.pos(tag)
+			w.string(tag.Value)
+		}
+		w.expr(guard.X)
+	} else {
+		w.expr(stmt.Tag)
+	}
 
 	w.len(len(stmt.Body))
 	for i, clause := range stmt.Body {
@@ -1207,6 +1217,19 @@
 		w.expr(expr.Y)
 
 	case *syntax.CallExpr:
+		tv, ok := w.p.info.Types[expr.Fun]
+		assert(ok)
+		if tv.IsType() {
+			assert(len(expr.ArgList) == 1)
+			assert(!expr.HasDots)
+
+			w.code(exprConvert)
+			w.typ(tv.Type)
+			w.pos(expr)
+			w.expr(expr.ArgList[0])
+			break
+		}
+
 		w.code(exprCall)
 
 		if inf, ok := w.p.info.Inferred[expr]; ok {
@@ -1223,15 +1246,6 @@
 		w.pos(expr)
 		w.exprs(expr.ArgList)
 		w.bool(expr.HasDots)
-
-	case *syntax.TypeSwitchGuard:
-		w.code(exprTypeSwitchGuard)
-		w.pos(expr)
-		if tag := expr.Lhs; w.bool(tag != nil) {
-			w.pos(tag)
-			w.string(tag.Value)
-		}
-		w.expr(expr.X)
 	}
 }