[dev.typeparams] cmd/compile: refactor top-level typechecking in unified IR

This CL is a first step towards incremental typechecking during IR
construction within unified IR. Namely, all top-level declarations are
now typechecked as they're constructed, except for assignments (which
aren't really declarations anyway).

Change-Id: I65763a7659bf2e0f5e89dfe9e709d60e0fa4c631
Reviewed-on: https://go-review.googlesource.com/c/go/+/332097
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 4b42ae1..24977ed 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -784,6 +784,8 @@
 	fn.Pragma = r.pragmaFlag()
 	r.linkname(name)
 
+	typecheck.Func(fn)
+
 	if r.bool() {
 		fn.ABI = obj.ABI(r.uint64())
 
@@ -2124,7 +2126,7 @@
 	// TODO(mdempsky): Use method.Pos instead?
 	pos := base.AutogeneratedPos
 
-	fn := r.newWrapperFunc(pos, sym, wrapper, method, target)
+	fn := r.newWrapperFunc(pos, sym, wrapper, method)
 
 	var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name)
 
@@ -2143,6 +2145,8 @@
 	}
 
 	addTailCall(pos, fn, recv, method)
+
+	r.finishWrapperFunc(fn, target)
 }
 
 func (r *reader) methodValueWrapper(tbase *types.Type, method *types.Field, target *ir.Package) {
@@ -2167,7 +2171,7 @@
 	// TODO(mdempsky): Use method.Pos instead?
 	pos := base.AutogeneratedPos
 
-	fn := r.newWrapperFunc(pos, sym, nil, method, target)
+	fn := r.newWrapperFunc(pos, sym, nil, method)
 	fn.SetNeedctxt(true)
 	sym.Def = fn
 
@@ -2181,9 +2185,11 @@
 	fn.ClosureVars = append(fn.ClosureVars, recv)
 
 	addTailCall(pos, fn, recv, method)
+
+	r.finishWrapperFunc(fn, target)
 }
 
-func (r *reader) newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field, target *ir.Package) *ir.Func {
+func (r *reader) newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func {
 	fn := ir.NewFunc(pos)
 	fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
 
@@ -2214,11 +2220,19 @@
 	defParams(ir.PPARAM, sig.Params())
 	defParams(ir.PPARAMOUT, sig.Results())
 
-	target.Decls = append(target.Decls, fn)
-
 	return fn
 }
 
+func (r *reader) finishWrapperFunc(fn *ir.Func, target *ir.Package) {
+	typecheck.Func(fn)
+
+	ir.WithFunc(fn, func() {
+		typecheck.Stmts(fn.Body)
+	})
+
+	target.Decls = append(target.Decls, fn)
+}
+
 // newWrapperType returns a copy of the given signature type, but with
 // the receiver parameter type substituted with recvType.
 // If recvType is nil, newWrapperType returns a signature
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 8397f14..03bcb27 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -109,6 +109,16 @@
 	r.ext = r
 	r.pkgInit(types.LocalPkg, target)
 
+	// Type-check any top-level assignments. We ignore non-assignments
+	// here because other declarations are typechecked as they're
+	// constructed.
+	for i, ndecls := 0, len(target.Decls); i < ndecls; i++ {
+		switch n := target.Decls[i]; n.Op() {
+		case ir.OAS, ir.OAS2:
+			target.Decls[i] = typecheck.Stmt(n)
+		}
+	}
+
 	// Don't use range--bodyIdx can add closures to todoBodies.
 	for len(todoBodies) > 0 {
 		// The order we expand bodies doesn't matter, so pop from the end
@@ -122,22 +132,12 @@
 
 		// Instantiated generic function: add to Decls for typechecking
 		// and compilation.
-		if pri.dict != nil && len(pri.dict.targs) != 0 && fn.OClosure == nil {
+		if fn.OClosure == nil && len(pri.dict.targs) != 0 {
 			target.Decls = append(target.Decls, fn)
 		}
 	}
 	todoBodies = nil
 
-	if !quirksMode() {
-		// TODO(mdempsky): Investigate generating wrappers in quirks mode too.
-		r.wrapTypes(target)
-	}
-
-	// Don't use range--typecheck can add closures to Target.Decls.
-	for i := 0; i < len(target.Decls); i++ {
-		target.Decls[i] = typecheck.Stmt(target.Decls[i])
-	}
-
 	// Don't use range--typecheck can add closures to Target.Decls.
 	for i := 0; i < len(target.Decls); i++ {
 		if fn, ok := target.Decls[i].(*ir.Func); ok {
@@ -145,8 +145,9 @@
 				s := fmt.Sprintf("\nbefore typecheck %v", fn)
 				ir.Dump(s, fn)
 			}
-			ir.CurFunc = fn
-			typecheck.Stmts(fn.Body)
+			ir.WithFunc(fn, func() {
+				typecheck.Stmts(fn.Body)
+			})
 			if base.Flag.W > 1 {
 				s := fmt.Sprintf("\nafter typecheck %v", fn)
 				ir.Dump(s, fn)
@@ -154,6 +155,26 @@
 		}
 	}
 
+	if !quirksMode() {
+		// TODO(mdempsky): Investigate generating wrappers in quirks mode too.
+		r.wrapTypes(target)
+	}
+
+	// Check that nothing snuck past typechecking.
+	for _, n := range target.Decls {
+		if n.Typecheck() == 0 {
+			base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
+		}
+
+		// For functions, check that at least their first statement (if
+		// any) was typechecked too.
+		if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 {
+			if stmt := fn.Body[0]; stmt.Typecheck() == 0 {
+				base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt)
+			}
+		}
+	}
+
 	base.ExitIfErrors() // just in case
 }