Merge "[dev.typeparams] all: merge master (37f9a8f) into dev.typeparams" into dev.typeparams
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index c0346c0..c94f19f 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -186,6 +186,7 @@
 	base.AutogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
 
 	typecheck.InitUniverse()
+	typecheck.InitRuntime()
 
 	// Parse and typecheck input.
 	noder.LoadPackage(flag.Args())
@@ -194,7 +195,6 @@
 
 	// Prepare for backend processing. This must happen before pkginit,
 	// because it generates itabs for initializing global variables.
-	typecheck.InitRuntime()
 	ssagen.InitConfig()
 
 	// Build init task.
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go
index 7291138..23e9446 100644
--- a/src/cmd/compile/internal/noder/linker.go
+++ b/src/cmd/compile/internal/noder/linker.go
@@ -209,8 +209,6 @@
 
 		pri, ok := bodyReader[name.Func]
 		assert(ok)
-		w.sync(syncAddBody)
-		w.sync(syncImplicitTypes)
 		w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx))
 	}
 
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index d2fe575..0423fcc 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -16,6 +16,7 @@
 	"cmd/compile/internal/deadcode"
 	"cmd/compile/internal/dwarfgen"
 	"cmd/compile/internal/ir"
+	"cmd/compile/internal/reflectdata"
 	"cmd/compile/internal/typecheck"
 	"cmd/compile/internal/types"
 	"cmd/internal/obj"
@@ -104,8 +105,9 @@
 	// separately so that it doesn't take up space in every reader
 	// instance.
 
-	curfn  *ir.Func
-	locals []*ir.Name
+	curfn       *ir.Func
+	locals      []*ir.Name
+	closureVars []*ir.Name
 
 	funarghack bool
 
@@ -419,7 +421,7 @@
 	if len(fields) == 0 {
 		return types.Types[types.TINTER] // empty interface
 	}
-	return types.NewInterface(tpkg, fields)
+	return r.needWrapper(types.NewInterface(tpkg, fields))
 }
 
 func (r *reader) structType() *types.Type {
@@ -440,7 +442,7 @@
 		}
 		fields[i] = f
 	}
-	return types.NewStruct(tpkg, fields)
+	return r.needWrapper(types.NewStruct(tpkg, fields))
 }
 
 func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type {
@@ -597,6 +599,10 @@
 			typ.Methods().Set(methods)
 		}
 
+		if !typ.IsPtr() {
+			r.needWrapper(typ)
+		}
+
 		return name
 
 	case objVar:
@@ -770,10 +776,10 @@
 				Cost:            int32(r.len()),
 				CanDelayResults: r.bool(),
 			}
-			r.addBody(name.Func)
+			r.addBody(name.Func, r.explicits)
 		}
 	} else {
-		r.addBody(name.Func)
+		r.addBody(name.Func, r.explicits)
 	}
 	r.sync(syncEOF)
 }
@@ -835,25 +841,7 @@
 // constructed.
 var todoBodies []*ir.Func
 
-// Keep in sync with writer.implicitTypes
-// Also see comment there for why r.implicits and r.explicits should
-// never both be non-empty.
-func (r *reader) implicitTypes() []*types.Type {
-	r.sync(syncImplicitTypes)
-
-	implicits := r.implicits
-	if len(implicits) == 0 {
-		implicits = r.explicits
-	} else {
-		assert(len(r.explicits) == 0)
-	}
-	return implicits
-}
-
-func (r *reader) addBody(fn *ir.Func) {
-	r.sync(syncAddBody)
-
-	implicits := r.implicitTypes()
+func (r *reader) addBody(fn *ir.Func, implicits []*types.Type) {
 	pri := pkgReaderIndex{r.p, r.reloc(relocBody), implicits}
 	bodyReader[fn] = pri
 
@@ -872,7 +860,7 @@
 
 func (r *reader) funcBody(fn *ir.Func) {
 	r.curfn = fn
-	r.locals = fn.ClosureVars
+	r.closureVars = fn.ClosureVars
 
 	// TODO(mdempsky): Get rid of uses of typecheck.NodAddrAt so we
 	// don't have to set ir.CurFunc.
@@ -999,7 +987,10 @@
 
 func (r *reader) useLocal() *ir.Name {
 	r.sync(syncUseObjLocal)
-	return r.locals[r.len()]
+	if r.bool() {
+		return r.locals[r.len()]
+	}
+	return r.closureVars[r.len()]
 }
 
 func (r *reader) openScope() {
@@ -1083,8 +1074,11 @@
 
 	case stmtAssign:
 		pos := r.pos()
-		names, lhs := r.assignList()
+
+		// TODO(mdempsky): After quirks mode is gone, swap these
+		// statements so we visit LHS before RHS again.
 		rhs := r.exprList()
+		names, lhs := r.assignList()
 
 		if len(rhs) == 0 {
 			for _, name := range names {
@@ -1220,8 +1214,12 @@
 
 	if r.bool() {
 		pos := r.pos()
-		names, lhs := r.assignList()
+
+		// TODO(mdempsky): After quirks mode is gone, swap these
+		// statements so we read LHS before X again.
 		x := r.expr()
+		names, lhs := r.assignList()
+
 		body := r.blockStmt()
 		r.closeAnotherScope()
 
@@ -1567,7 +1565,7 @@
 		r.setType(cv, outer.Type())
 	}
 
-	r.addBody(fn)
+	r.addBody(fn, r.implicits)
 
 	return fn.OClosure
 }
@@ -1772,8 +1770,9 @@
 	r.inlTreeIndex = inlIndex
 	r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
 
-	for _, cv := range r.inlFunc.ClosureVars {
-		r.locals = append(r.locals, cv.Outer)
+	r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
+	for i, cv := range r.inlFunc.ClosureVars {
+		r.closureVars[i] = cv.Outer
 	}
 
 	r.funcargs(fn)
@@ -2015,3 +2014,184 @@
 	})
 	return used
 }
+
+// @@@ Method wrappers
+
+// needWrapperTypes lists types for which we may need to generate
+// method wrappers.
+var needWrapperTypes []*types.Type
+
+func (r *reader) needWrapper(typ *types.Type) *types.Type {
+	// TODO(mdempsky): Be more judicious about generating wrappers.
+	// For now, generating all possible wrappers is simple and correct,
+	// but potentially wastes a lot of time/space.
+
+	if typ.IsPtr() {
+		base.Fatalf("bad pointer type: %v", typ)
+	}
+
+	needWrapperTypes = append(needWrapperTypes, typ)
+	return typ
+}
+
+func (r *reader) wrapTypes(target *ir.Package) {
+	// always generate a wrapper for error.Error (#29304)
+	r.needWrapper(types.ErrorType)
+
+	seen := make(map[string]*types.Type)
+	for _, typ := range needWrapperTypes {
+		if typ.Sym() == nil {
+			key := typ.ShortString()
+			if prev := seen[key]; prev != nil {
+				if !types.Identical(typ, prev) {
+					base.Fatalf("collision: types %v and %v have short string %q", typ, prev, key)
+				}
+				continue
+			}
+			seen[key] = typ
+		}
+
+		r.wrapType(typ, target)
+	}
+
+	needWrapperTypes = nil
+}
+
+func (r *reader) wrapType(typ *types.Type, target *ir.Package) {
+	if !typ.IsInterface() {
+		typecheck.CalcMethods(typ)
+	}
+	for _, meth := range typ.AllMethods().Slice() {
+		if meth.Sym.IsBlank() || !meth.IsMethod() {
+			base.FatalfAt(meth.Pos, "invalid method: %v", meth)
+		}
+
+		r.methodWrapper(0, typ, meth, target)
+
+		// For non-interface types, we also want *T wrappers.
+		if !typ.IsInterface() {
+			r.methodWrapper(1, typ, meth, target)
+
+			// For not-in-heap types, *T is a scalar, not pointer shaped,
+			// so the interface wrappers use **T.
+			if typ.NotInHeap() {
+				r.methodWrapper(2, typ, meth, target)
+			}
+		}
+	}
+}
+
+func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) {
+	wrapper := tbase
+	for i := 0; i < derefs; i++ {
+		wrapper = types.NewPtr(wrapper)
+	}
+
+	sym := ir.MethodSym(wrapper, method.Sym)
+	assert(!sym.Siggen())
+	sym.SetSiggen(true)
+
+	wrappee := method.Type.Recv().Type
+	if types.Identical(wrapper, wrappee) ||
+		!types.IsMethodApplicable(wrapper, method) ||
+		!reflectdata.NeedEmit(tbase) {
+		return
+	}
+
+	// TODO(mdempsky): Use method.Pos instead?
+	pos := base.AutogeneratedPos
+
+	fn := ir.NewFunc(pos)
+	fn.SetDupok(true)   // TODO(mdempsky): Leave unset for local, non-generic wrappers?
+	fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
+
+	fn.Nname = ir.NewNameAt(pos, sym)
+	ir.MarkFunc(fn.Nname)
+	fn.Nname.Func = fn
+	fn.Nname.Defn = fn
+
+	sig := newWrapperType(wrapper, method.Type)
+	r.setType(fn.Nname, sig)
+
+	// TODO(mdempsky): De-duplicate with similar logic in funcargs.
+	defParams := func(class ir.Class, params ...*types.Field) {
+		for _, param := range params {
+			name := ir.NewNameAt(param.Pos, param.Sym)
+			name.Class = class
+			r.setType(name, param.Type)
+
+			name.Curfn = fn
+			fn.Dcl = append(fn.Dcl, name)
+
+			param.Nname = name
+		}
+	}
+
+	defParams(ir.PPARAM, sig.Recv())
+	defParams(ir.PPARAM, sig.Params().FieldSlice()...)
+	defParams(ir.PPARAMOUT, sig.Results().FieldSlice()...)
+
+	var recv ir.Node = sig.Recv().Nname.(*ir.Name)
+
+	// For simple *T wrappers around T methods, panicwrap produces a
+	// nicer panic message.
+	if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) {
+		cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node))
+		then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)}
+		fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil))
+	}
+
+	// Add implicit derefs, as necessary. typecheck will add one deref,
+	// but not-in-heap types will need another for their **T wrappers.
+	for i := 0; i < derefs; i++ {
+		recv = Implicit(ir.NewStarExpr(pos, recv))
+	}
+
+	args := make([]ir.Node, sig.NumParams())
+	for i, param := range sig.Params().FieldSlice() {
+		args[i] = param.Nname.(*ir.Name)
+	}
+
+	fn.Body.Append(newTailCall(pos, method, recv, args))
+
+	target.Decls = append(target.Decls, fn)
+}
+
+// newWrapperType returns a copy of the given signature type, but with
+// the receiver parameter type substituted with wrapper.
+func newWrapperType(wrapper, sig *types.Type) *types.Type {
+	clone := func(params []*types.Field) []*types.Field {
+		res := make([]*types.Field, len(params))
+		for i, param := range params {
+			sym := param.Sym
+			if sym == nil || sym.Name == "_" {
+				sym = typecheck.LookupNum(".anon", i)
+			}
+			res[i] = types.NewField(param.Pos, sym, param.Type)
+			res[i].SetIsDDD(param.IsDDD())
+		}
+		return res
+	}
+
+	recv := types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), wrapper)
+	params := clone(sig.Params().FieldSlice())
+	results := clone(sig.Results().FieldSlice())
+
+	return types.NewSignature(types.NoPkg, recv, nil, params, results)
+}
+
+func newTailCall(pos src.XPos, method *types.Field, recv ir.Node, args []ir.Node) ir.Node {
+	// TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
+	// Not urgent though, because tail calls are currently incompatible with regabi anyway.
+
+	call := ir.NewCallExpr(pos, ir.OCALL, ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym), args)
+	call.IsDDD = method.Type.IsVariadic()
+
+	if method.Type.NumResults() == 0 {
+		return call
+	}
+
+	ret := ir.NewReturnStmt(pos, nil)
+	ret.Results = []ir.Node{call}
+	return ret
+}
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 7a1bb88..292fd13 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -74,6 +74,8 @@
 
 	if !quirksMode() {
 		writeNewExportFunc = writeNewExport
+	} else if base.Flag.G != 0 {
+		base.Errorf("cannot use -G and -d=quirksmode together")
 	}
 
 	newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
@@ -126,6 +128,11 @@
 	}
 	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])
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index 889a96e..0496910 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -97,7 +97,10 @@
 	explicitIdx map[*types2.TypeParam]int
 
 	// variables declared within this function
-	localsIdx map[types2.Object]int
+	localsIdx map[*types2.Var]int
+
+	closureVars    []posObj
+	closureVarsIdx map[*types2.Var]int
 }
 
 func (pw *pkgWriter) newWriter(k reloc, marker syncMarker) *writer {
@@ -626,11 +629,15 @@
 		}
 	}
 
+	sig, block := obj.Type().(*types2.Signature), decl.Body
+	body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.explicitIdx)
+	assert(len(closureVars) == 0)
+
 	w.sync(syncFuncExt)
 	w.pragmaFlag(pragma)
 	w.linkname(obj)
 	w.bool(false) // stub extension
-	w.addBody(obj.Type().(*types2.Signature), decl.Body, make(map[types2.Object]int))
+	w.reloc(relocBody, body)
 	w.sync(syncEOF)
 }
 
@@ -665,41 +672,9 @@
 
 // @@@ Function bodies
 
-func (w *writer) implicitTypes() map[*types2.TypeParam]int {
-	w.sync(syncImplicitTypes)
-
-	// TODO(mdempsky): Theoretically, I think at this point we want to
-	// extend the implicit type parameters list with any new explicit
-	// type parameters.
-	//
-	// However, I believe that's moot: declared functions and methods
-	// have explicit type parameters, but are always declared at package
-	// scope (which has no implicit type parameters); and function
-	// literals can appear within a type-parameterized function (i.e.,
-	// implicit type parameters), but cannot have explicit type
-	// parameters of their own.
-	//
-	// So I think it's safe to just use whichever is non-empty.
-	implicitIdx := w.implicitIdx
-	if len(implicitIdx) == 0 {
-		implicitIdx = w.explicitIdx
-	} else {
-		assert(len(w.explicitIdx) == 0)
-	}
-	return implicitIdx
-}
-
-func (w *writer) addBody(sig *types2.Signature, block *syntax.BlockStmt, localsIdx map[types2.Object]int) {
-	w.sync(syncAddBody)
-
-	implicits := w.implicitTypes()
-	w.reloc(relocBody, w.p.bodyIdx(w.p.curpkg, sig, block, implicits, localsIdx))
-}
-
-func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, implicitIdx map[*types2.TypeParam]int, localsIdx map[types2.Object]int) int {
+func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, implicitIdx map[*types2.TypeParam]int) (idx int, closureVars []posObj) {
 	w := pw.newWriter(relocBody, syncFuncBody)
 	w.implicitIdx = implicitIdx
-	w.localsIdx = localsIdx
 
 	w.funcargs(sig)
 	if w.bool(block != nil) {
@@ -707,7 +682,7 @@
 		w.pos(block.Rbrace)
 	}
 
-	return w.flush()
+	return w.flush(), w.closureVars
 }
 
 func (w *writer) funcargs(sig *types2.Signature) {
@@ -730,19 +705,35 @@
 	}
 }
 
-func (w *writer) addLocal(obj types2.Object) {
+func (w *writer) addLocal(obj *types2.Var) {
 	w.sync(syncAddLocal)
 	idx := len(w.localsIdx)
 	if enableSync {
 		w.int(idx)
 	}
+	if w.localsIdx == nil {
+		w.localsIdx = make(map[*types2.Var]int)
+	}
 	w.localsIdx[obj] = idx
 }
 
-func (w *writer) useLocal(obj types2.Object) {
+func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
 	w.sync(syncUseObjLocal)
-	idx, ok := w.localsIdx[obj]
-	assert(ok)
+
+	if idx, ok := w.localsIdx[obj]; w.bool(ok) {
+		w.len(idx)
+		return
+	}
+
+	idx, ok := w.closureVarsIdx[obj]
+	if !ok {
+		if w.closureVarsIdx == nil {
+			w.closureVarsIdx = make(map[*types2.Var]int)
+		}
+		idx = len(w.closureVars)
+		w.closureVars = append(w.closureVars, posObj{pos, obj})
+		w.closureVarsIdx[obj] = idx
+	}
 	w.len(idx)
 }
 
@@ -806,8 +797,8 @@
 		default:
 			w.code(stmtAssign)
 			w.pos(stmt)
-			w.assignList(stmt.Lhs)
 			w.exprList(stmt.Rhs)
+			w.assignList(stmt.Lhs)
 		}
 
 	case *syntax.BlockStmt:
@@ -877,6 +868,8 @@
 	for _, expr := range exprs {
 		if name, ok := expr.(*syntax.Name); ok && name.Value != "_" {
 			if obj, ok := w.p.info.Defs[name]; ok {
+				obj := obj.(*types2.Var)
+
 				w.bool(true)
 				w.pos(obj)
 				w.localIdent(obj)
@@ -923,16 +916,16 @@
 			for i, name := range decl.NameList {
 				w.code(stmtAssign)
 				w.pos(decl)
-				w.assignList(name)
 				w.exprList(values[i])
+				w.assignList(name)
 			}
 			break
 		}
 
 		w.code(stmtAssign)
 		w.pos(decl)
-		w.assignList(namesAsExpr(decl.NameList))
 		w.exprList(decl.Values)
+		w.assignList(namesAsExpr(decl.NameList))
 	}
 }
 
@@ -949,8 +942,8 @@
 
 	if rang, ok := stmt.Init.(*syntax.RangeClause); w.bool(ok) {
 		w.pos(rang)
-		w.assignList(rang.Lhs)
 		w.expr(rang.X)
+		w.assignList(rang.Lhs)
 	} else {
 		w.pos(stmt)
 		w.stmt(stmt.Init)
@@ -1092,15 +1085,17 @@
 	}
 
 	if obj != nil {
-		if _, ok := w.localsIdx[obj]; ok {
-			assert(len(targs) == 0)
-			w.code(exprLocal)
-			w.useLocal(obj)
+		if isGlobal(obj) {
+			w.code(exprName)
+			w.obj(obj, targs)
 			return
 		}
 
-		w.code(exprName)
-		w.obj(obj, targs)
+		obj := obj.(*types2.Var)
+		assert(len(targs) == 0)
+
+		w.code(exprLocal)
+		w.useLocal(expr.Pos(), obj)
 		return
 	}
 
@@ -1248,106 +1243,24 @@
 	w.pos(expr.Type) // for QuirksMode
 	w.signature(sig)
 
-	closureVars, localsIdx := w.captureVars(expr)
+	block := expr.Body
+	body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.implicitIdx)
+
 	w.len(len(closureVars))
-	for _, closureVar := range closureVars {
-		w.pos(closureVar.pos)
-		w.useLocal(closureVar.obj)
+	for _, cv := range closureVars {
+		w.pos(cv.pos)
+		if quirksMode() {
+			cv.pos = expr.Body.Rbrace
+		}
+		w.useLocal(cv.pos, cv.obj)
 	}
 
-	w.addBody(sig, expr.Body, localsIdx)
+	w.reloc(relocBody, body)
 }
 
 type posObj struct {
 	pos syntax.Pos
-	obj types2.Object
-}
-
-// captureVars returns the free variables used by the given function
-// literal.
-func (w *writer) captureVars(expr *syntax.FuncLit) (closureVars []posObj, localsIdx map[types2.Object]int) {
-	scope, ok := w.p.info.Scopes[expr.Type]
-	assert(ok)
-
-	localsIdx = make(map[types2.Object]int)
-
-	// TODO(mdempsky): This code needs to be cleaned up (e.g., to avoid
-	// traversing nested function literals multiple times). This will be
-	// easier after we drop quirks mode.
-
-	var rbracePos syntax.Pos
-
-	var visitor func(n syntax.Node) bool
-	visitor = func(n syntax.Node) bool {
-
-		// Constant expressions don't count towards capturing.
-		if n, ok := n.(syntax.Expr); ok {
-			if tv, ok := w.p.info.Types[n]; ok && tv.Value != nil {
-				return true
-			}
-		}
-
-		switch n := n.(type) {
-		case *syntax.Name:
-			if obj, ok := w.p.info.Uses[n].(*types2.Var); ok && !obj.IsField() && obj.Pkg() == w.p.curpkg && obj.Parent() != obj.Pkg().Scope() {
-				// Found a local variable. See if it chains up to scope.
-				parent := obj.Parent()
-				for {
-					if parent == scope {
-						break
-					}
-					if parent == obj.Pkg().Scope() {
-						if _, present := localsIdx[obj]; !present {
-							pos := rbracePos
-							if pos == (syntax.Pos{}) {
-								pos = n.Pos()
-							}
-
-							idx := len(closureVars)
-							closureVars = append(closureVars, posObj{pos, obj})
-							localsIdx[obj] = idx
-						}
-						break
-					}
-					parent = parent.Parent()
-				}
-			}
-
-		case *syntax.FuncLit:
-			// Quirk: typecheck uses the rbrace position position of the
-			// function literal as the position of the intermediary capture.
-			if quirksMode() && rbracePos == (syntax.Pos{}) {
-				rbracePos = n.Body.Rbrace
-				syntax.Crawl(n.Body, visitor)
-				rbracePos = syntax.Pos{}
-				return true
-			}
-
-		case *syntax.AssignStmt:
-			// Quirk: typecheck visits (and thus captures) the RHS of
-			// assignment statements before the LHS.
-			if quirksMode() && (n.Op == 0 || n.Op == syntax.Def) {
-				syntax.Crawl(n.Rhs, visitor)
-				syntax.Crawl(n.Lhs, visitor)
-				return true
-			}
-		case *syntax.RangeClause:
-			// Quirk: Similarly, it visits the expression to be iterated
-			// over before the iteration variables.
-			if quirksMode() {
-				syntax.Crawl(n.X, visitor)
-				if n.Lhs != nil {
-					syntax.Crawl(n.Lhs, visitor)
-				}
-				return true
-			}
-		}
-
-		return false
-	}
-	syntax.Crawl(expr.Body, visitor)
-
-	return
+	obj *types2.Var
 }
 
 func (w *writer) exprList(expr syntax.Expr) {
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index 8378fab..8421e36 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -927,29 +927,27 @@
 	if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
 		tbase = t.Elem()
 	}
+	if tbase.Kind() == types.TFORW {
+		base.Fatalf("unresolved defined type: %v", tbase)
+	}
+
 	dupok := 0
-	if tbase.Sym() == nil {
+	if tbase.Sym() == nil { // TODO(mdempsky): Probably need DUPOK for instantiated types too.
 		dupok = obj.DUPOK
 	}
 
-	if base.Ctxt.Pkgpath != "runtime" || (tbase != types.Types[tbase.Kind()] && tbase != types.ByteType && tbase != types.RuneType && tbase != types.ErrorType) { // int, float, etc
-		// Named types from other files are defined only by those files.
-		// However, as an exception, we can write out instantiated types
-		// in the local package, even if they may be marked as part of
-		// another package (the package of their base generic type).
-		if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg &&
-			!tbase.IsFullyInstantiated() {
-			if i := typecheck.BaseTypeIndex(t); i >= 0 {
-				lsym.Pkg = tbase.Sym().Pkg.Prefix
-				lsym.SymIdx = int32(i)
-				lsym.Set(obj.AttrIndexed, true)
-			}
-			return lsym
+	if !NeedEmit(tbase) {
+		if i := typecheck.BaseTypeIndex(t); i >= 0 {
+			lsym.Pkg = tbase.Sym().Pkg.Prefix
+			lsym.SymIdx = int32(i)
+			lsym.Set(obj.AttrIndexed, true)
 		}
-		// TODO(mdempsky): Investigate whether this can happen.
-		if tbase.Kind() == types.TFORW {
-			return lsym
-		}
+
+		// TODO(mdempsky): Investigate whether this still happens.
+		// If we know we don't need to emit code for a type,
+		// we should have a link-symbol index for it.
+		// See also TODO in NeedEmit.
+		return lsym
 	}
 
 	ot := 0
@@ -1678,6 +1676,44 @@
 	}
 }
 
+// NeedEmit reports whether typ is a type that we need to emit code
+// for (e.g., runtime type descriptors, method wrappers).
+func NeedEmit(typ *types.Type) bool {
+	// TODO(mdempsky): Export data should keep track of which anonymous
+	// and instantiated types were emitted, so at least downstream
+	// packages can skip re-emitting them.
+	//
+	// Perhaps we can just generalize the linker-symbol indexing to
+	// track the index of arbitrary types, not just defined types, and
+	// use its presence to detect this. The same idea would work for
+	// instantiated generic functions too.
+
+	switch sym := typ.Sym(); {
+	case sym == nil:
+		// Anonymous type; possibly never seen before or ever again.
+		// Need to emit to be safe (however, see TODO above).
+		return true
+
+	case sym.Pkg == types.LocalPkg:
+		// Local defined type; our responsibility.
+		return true
+
+	case base.Ctxt.Pkgpath == "runtime" && (sym.Pkg == types.BuiltinPkg || sym.Pkg == ir.Pkgs.Unsafe):
+		// Package runtime is responsible for including code for builtin
+		// types (predeclared and package unsafe).
+		return true
+
+	case typ.IsFullyInstantiated():
+		// Instantiated type; possibly instantiated with unique type arguments.
+		// Need to emit to be safe (however, see TODO above).
+		return true
+
+	default:
+		// Should have been emitted by an imported package.
+		return false
+	}
+}
+
 // Generate a wrapper function to convert from
 // a receiver of type T to a receiver of type U.
 // That is,
@@ -1724,10 +1760,6 @@
 		// TODO: check that we do the right thing when method is an interface method.
 		generic = true
 	}
-	if base.Debug.Unified != 0 {
-		// TODO(mdempsky): Support dictionaries for unified IR.
-		generic = false
-	}
 	newnam := ir.MethodSym(rcvr, method.Sym)
 	lsym := newnam.Linksym()
 	if newnam.Siggen() {
@@ -1735,28 +1767,17 @@
 	}
 	newnam.SetSiggen(true)
 
+	// Except in quirks mode, unified IR creates its own wrappers.
+	// Complain loudly if it missed any.
+	if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
+		base.FatalfAt(method.Pos, "missing wrapper for %+v (%+v, %v) / %+v / %+v", rcvr, orig, types.IsDirectIface(orig), method.Sym, newnam)
+	}
+
 	if !generic && types.Identical(rcvr, method.Type.Recv().Type) {
 		return lsym
 	}
 
-	// imported reports whether typ is a defined type that was declared
-	// in an imported package, and therefore must have been compiled in
-	// that package.
-	importedType := func(typ *types.Type) bool {
-		return typ.Sym() != nil && typ.Sym().Pkg != types.LocalPkg &&
-
-			// Exception: need wrapper for error.Error (#29304).
-			// TODO(mdempsky): Put this in package runtime, like we do for
-			// the type descriptors for predeclared types.
-			typ != types.ErrorType &&
-
-			// Exception: parameterized types may have been instantiated
-			// with new type arguments, so we don't assume they've been
-			// compiled before.
-			!typ.IsFullyInstantiated()
-	}
-
-	if importedType(rcvr) || rcvr.IsPtr() && importedType(rcvr.Elem()) {
+	if !NeedEmit(rcvr) || rcvr.IsPtr() && !NeedEmit(rcvr.Elem()) {
 		return lsym
 	}
 
diff --git a/test/fixedbugs/issue46903.go b/test/fixedbugs/issue46903.go
new file mode 100644
index 0000000..3237a58
--- /dev/null
+++ b/test/fixedbugs/issue46903.go
@@ -0,0 +1,32 @@
+// run
+//go:build goexperiment.unified
+// +build goexperiment.unified
+
+// TODO(mdempsky): Enable test unconditionally. This test should pass
+// for non-unified mode too.
+
+// 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
+
+//go:notinheap
+type A struct{ B }
+type B struct{ x byte }
+type I interface{ M() *B }
+
+func (p *B) M() *B { return p }
+
+var (
+	a A
+	i I = &a
+)
+
+func main() {
+	got, want := i.M(), &a.B
+	if got != want {
+		println(got, "!=", want)
+		panic("FAIL")
+	}
+}
diff --git a/test/typeparam/issue44688.go b/test/typeparam/issue44688.go
index d70f94f..de1140b 100644
--- a/test/typeparam/issue44688.go
+++ b/test/typeparam/issue44688.go
@@ -1,6 +1,4 @@
 // run -gcflags=-G=3
-//go:build goexperiment.unified
-// +build !goexperiment.unified
 
 // Copyright 2021 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style