Skip to content
Snippets Groups Projects
reader.go 107 KiB
Newer Older
  • Learn to ignore specific revisions
  • 						for len(rtypes) < i {
    							rtypes = append(rtypes, nil)
    						}
    						rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), types.Types[types.TBOOL]))
    
    
    		clause := ir.NewCaseStmt(pos, cases, nil)
    
    			name := ir.NewNameAt(pos, ident.Sym())
    
    			r.addLocal(name, ir.PAUTO)
    			clause.Var = name
    
    		}
    
    		clause.Body = r.stmts()
    		clauses[i] = clause
    	}
    	if len(clauses) > 0 {
    		r.closeScope()
    	}
    	r.closeScope()
    
    	n := ir.NewSwitchStmt(pos, tag, clauses)
    	n.Label = label
    	if init != nil {
    		n.SetInit([]ir.Node{init})
    	}
    	return n
    }
    
    func (r *reader) label() *types.Sym {
    
    	r.Sync(pkgbits.SyncLabel)
    	name := r.String()
    
    	if r.inlCall != nil {
    		name = fmt.Sprintf("~%s·%d", name, inlgen)
    	}
    	return typecheck.Lookup(name)
    }
    
    func (r *reader) optLabel() *types.Sym {
    
    	r.Sync(pkgbits.SyncOptLabel)
    	if r.Bool() {
    
    		return r.label()
    	}
    	return nil
    }
    
    // initDefn marks the given names as declared by defn and populates
    // its Init field with ODCL nodes. It then reports whether any names
    // were so declared, which can be used to initialize defn.Def.
    func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool {
    	if len(names) == 0 {
    		return false
    	}
    
    	init := make([]ir.Node, len(names))
    	for i, name := range names {
    		name.Defn = defn
    		init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
    	}
    	defn.SetInit(init)
    	return true
    }
    
    // @@@ Expressions
    
    
    // expr reads and returns a typechecked expression.
    
    func (r *reader) expr() (res ir.Node) {
    	defer func() {
    		if res != nil && res.Typecheck() == 0 {
    			base.FatalfAt(res.Pos(), "%v missed typecheck", res)
    		}
    	}()
    
    	switch tag := codeExpr(r.Code(pkgbits.SyncExpr)); tag {
    
    	default:
    		panic("unhandled expression")
    
    	case exprLocal:
    
    		// Callee instead of Expr allows builtins
    		// TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
    		return typecheck.Callee(r.obj())
    
    		pos := r.pos()
    		wrapperFn, baseFn, dictPtr := r.funcInst(pos)
    		if wrapperFn != nil {
    			return wrapperFn
    		}
    		return r.curry(pos, false, baseFn, dictPtr, nil)
    
    	case exprConst:
    		pos := r.pos()
    
    		val := FixValue(typ, r.Value())
    
    		return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
    
    	case exprNil:
    		pos := r.pos()
    		typ := r.typ()
    		return Nil(pos, typ)
    
    
    	case exprCompLit:
    		return r.compLit()
    
    	case exprFuncLit:
    		return r.funcLit()
    
    
    	case exprFieldVal:
    		x := r.expr()
    		pos := r.pos()
    		_, sym := r.selector()
    
    		return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
    
    	case exprMethodVal:
    
    		wrapperFn, baseFn, dictPtr := r.methodExpr()
    
    		// For simple wrapperFn values, the existing machinery for creating
    		// and deduplicating wrapperFn value wrappers still works fine.
    		if wrapperFn, ok := wrapperFn.(*ir.SelectorExpr); ok && wrapperFn.Op() == ir.OMETHEXPR {
    			// The receiver expression we constructed may have a shape type.
    			// For example, in fixedbugs/issue54343.go, `New[int]()` is
    			// constructed as `New[go.shape.int](&.dict.New[int])`, which
    			// has type `*T[go.shape.int]`, not `*T[int]`.
    			//
    			// However, the method we want to select here is `(*T[int]).M`,
    			// not `(*T[go.shape.int]).M`, so we need to manually convert
    			// the type back so that the OXDOT resolves correctly.
    			//
    			// TODO(mdempsky): Logically it might make more sense for
    			// exprCall to take responsibility for setting a non-shaped
    			// result type, but this is the only place where we care
    			// currently. And only because existing ir.OMETHVALUE backend
    			// code relies on n.X.Type() instead of n.Selection.Recv().Type
    			// (because the latter is types.FakeRecvType() in the case of
    			// interface method values).
    			//
    			if recv.Type().HasShape() {
    				typ := wrapperFn.Type().Params().Field(0).Type
    				if !types.Identical(typ, recv.Type()) {
    					base.FatalfAt(wrapperFn.Pos(), "receiver %L does not match %L", recv, wrapperFn)
    				}
    				recv = typecheck.Expr(ir.NewConvExpr(recv.Pos(), ir.OCONVNOP, typ, recv))
    			}
    
    			n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, recv, wrapperFn.Sel)).(*ir.SelectorExpr)
    			assert(n.Selection == wrapperFn.Selection)
    
    			wrapper := methodValueWrapper{
    				rcvr:   n.X.Type(),
    				method: n.Selection,
    			}
    
    			if r.importedDef() {
    				haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper)
    			} else {
    				needMethodValueWrappers = append(needMethodValueWrappers, wrapper)
    			}
    			return n
    
    
    		// For more complicated method expressions, we construct a
    		// function literal wrapper.
    		return r.curry(pos, true, baseFn, recv, dictPtr)
    
    		recv := r.typ()
    
    		implicits := make([]int, r.Len())
    		for i := range implicits {
    			implicits[i] = r.Len()
    		}
    		var deref, addr bool
    		if r.Bool() {
    			deref = true
    		} else if r.Bool() {
    			addr = true
    		}
    
    
    		wrapperFn, baseFn, dictPtr := r.methodExpr()
    
    		// If we already have a wrapper and don't need to do anything with
    		// it, we can just return the wrapper directly.
    		//
    		// N.B., we use implicits/deref/addr here as the source of truth
    		// rather than types.Identical, because the latter can be confused
    		// by tricky promoted methods (e.g., typeparam/mdempsky/21.go).
    		if wrapperFn != nil && len(implicits) == 0 && !deref && !addr {
    			if !types.Identical(recv, wrapperFn.Type().Params().Field(0).Type) {
    				base.FatalfAt(pos, "want receiver type %v, but have method %L", recv, wrapperFn)
    			}
    			return wrapperFn
    		}
    
    		// Otherwise, if the wrapper function is a static method
    		// expression (OMETHEXPR) and the receiver type is unshaped, then
    		// we can rely on a statically generated wrapper being available.
    		if method, ok := wrapperFn.(*ir.SelectorExpr); ok && method.Op() == ir.OMETHEXPR && !recv.HasShape() {
    			return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), method.Sel)).(*ir.SelectorExpr)
    		}
    
    		return r.methodExprWrap(pos, recv, implicits, deref, addr, baseFn, dictPtr)
    
    	case exprIndex:
    		x := r.expr()
    		pos := r.pos()
    		index := r.expr()
    
    		n := typecheck.Expr(ir.NewIndexExpr(pos, x, index))
    		switch n.Op() {
    		case ir.OINDEXMAP:
    			n := n.(*ir.IndexExpr)
    
    
    	case exprSlice:
    		x := r.expr()
    		pos := r.pos()
    		var index [3]ir.Node
    		for i := range index {
    
    		}
    		op := ir.OSLICE
    		if index[2] != nil {
    			op = ir.OSLICE3
    		}
    
    		return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2]))
    
    
    	case exprAssert:
    		x := r.expr()
    		pos := r.pos()
    
    		// TODO(mdempsky): Always emit ODYNAMICDOTTYPE for uniformity?
    
    		if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE {
    
    			assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType)
    
    			assert.ITab = typ.ITab
    			return typed(typ.Type(), assert)
    
    		return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ.Type()))
    
    
    	case exprUnaryOp:
    		op := r.op()
    		pos := r.pos()
    		x := r.expr()
    
    		switch op {
    		case ir.OADDR:
    
    			return typecheck.Expr(typecheck.NodAddrAt(pos, x))
    
    			return typecheck.Expr(ir.NewStarExpr(pos, x))
    
    		return typecheck.Expr(ir.NewUnaryExpr(pos, op, x))
    
    
    	case exprBinaryOp:
    		op := r.op()
    		x := r.expr()
    		pos := r.pos()
    		y := r.expr()
    
    		switch op {
    		case ir.OANDAND, ir.OOROR:
    
    			return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y))
    
    		return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
    
    	case exprRecv:
    		x := r.expr()
    		pos := r.pos()
    		for i, n := 0, r.Len(); i < n; i++ {
    			x = Implicit(DotField(pos, x, r.Len()))
    		}
    		if r.Bool() { // needs deref
    			x = Implicit(Deref(pos, x.Type().Elem(), x))
    		} else if r.Bool() { // needs addr
    			x = Implicit(Addr(pos, x))
    		}
    		return x
    
    
    		if r.Bool() { // method call
    
    			recv := r.expr()
    			_, method, dictPtr := r.methodExpr()
    
    			if recv.Type().IsInterface() && method.Op() == ir.OMETHEXPR {
    				method := method.(*ir.SelectorExpr)
    
    				// The compiler backend (e.g., devirtualization) handle
    				// OCALLINTER/ODOTINTER better than OCALLFUNC/OMETHEXPR for
    				// interface calls, so we prefer to continue constructing
    				// calls that way where possible.
    				//
    				// There are also corner cases where semantically it's perhaps
    				// significant; e.g., fixedbugs/issue15975.go, #38634, #52025.
    
    				fun = typecheck.Callee(ir.NewSelectorExpr(method.Pos(), ir.OXDOT, recv, method.Sel))
    			} else {
    				if recv.Type().IsInterface() {
    					// N.B., this happens currently for typeparam/issue51521.go
    					// and typeparam/typeswitch3.go.
    					if base.Flag.LowerM > 0 {
    						base.WarnfAt(method.Pos(), "imprecise interface call")
    					}
    				}
    
    				fun = method
    				args.Append(recv)
    			}
    			if dictPtr != nil {
    				args.Append(dictPtr)
    			}
    		} else if r.Bool() { // call to instanced function
    
    			_, shapedFn, dictPtr := r.funcInst(pos)
    			fun = shapedFn
    			args.Append(dictPtr)
    		} else {
    			fun = r.expr()
    
    		n := typecheck.Call(pos, fun, args, dots)
    		switch n.Op() {
    		case ir.OAPPEND:
    			n := n.(*ir.CallExpr)
    
    			// For append(a, b...), we don't need the implicit conversion. The typechecker already
    			// ensured that a and b are both slices with the same base type, or []byte and string.
    			if n.IsDDD {
    				if conv, ok := n.Args[1].(*ir.ConvExpr); ok && conv.Op() == ir.OCONVNOP && conv.Implicit() {
    					n.Args[1] = conv.X
    				}
    			}
    
    		case ir.OUNSAFESLICE:
    			n := n.(*ir.BinaryExpr)
    
    		n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr)
    
    		return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
    
    
    	case exprReshape:
    		typ := r.typ()
    		x := r.expr()
    
    		if types.IdenticalStrict(x.Type(), typ) {
    			return x
    		}
    
    		// Comparison expressions are constructed as "untyped bool" still.
    		//
    		// TODO(mdempsky): It should be safe to reshape them here too, but
    		// maybe it's better to construct them with the proper type
    		// instead.
    		if x.Type() == types.UntypedBool && typ.IsBoolean() {
    			return x
    		}
    
    		base.AssertfAt(x.Type().HasShape() || typ.HasShape(), x.Pos(), "%L and %v are not shape types", x, typ)
    		base.AssertfAt(types.Identical(x.Type(), typ), x.Pos(), "%L is not shape-identical to %v", x, typ)
    
    		// We use ir.HasUniquePos here as a check that x only appears once
    		// in the AST, so it's okay for us to call SetType without
    		// breaking any other uses of it.
    		//
    		// Notably, any ONAMEs should already have the exactly right shape
    		// type and been caught by types.IdenticalStrict above.
    		base.AssertfAt(ir.HasUniquePos(x), x.Pos(), "cannot call SetType(%v) on %L", typ, x)
    
    		if base.Debug.Reshape != 0 {
    			base.WarnfAt(x.Pos(), "reshaping %L to %v", x, typ)
    		}
    
    		x.SetType(typ)
    		return x
    
    
    		typeWord, srcRType := r.convRTTI(pos)
    
    
    		// TODO(mdempsky): Stop constructing expressions of untyped type.
    		x = typecheck.DefaultLit(x, typ)
    
    		if op, why := typecheck.Convertop(x.Op() == ir.OLITERAL, x.Type(), typ); op == ir.OXXX {
    			// types2 ensured that x is convertable to typ under standard Go
    			// semantics, but cmd/compile also disallows some conversions
    			// involving //go:notinheap.
    			//
    			// TODO(mdempsky): This can be removed after #46731 is implemented.
    			base.ErrorfAt(pos, "cannot convert %L to type %v%v", x, typ, why)
    			base.ErrorExit() // harsh, but prevents constructing invalid IR
    		}
    
    
    		ce := ir.NewConvExpr(pos, ir.OCONV, typ, x)
    		ce.TypeWord, ce.SrcRType = typeWord, srcRType
    
    		// Conversions between non-identical, non-empty interfaces always
    		// requires a runtime call, even if they have identical underlying
    		// interfaces. This is because we create separate itab instances
    		// for each unique interface type, not merely each unique
    		// interface shape.
    		//
    		// However, due to shape types, typecheck.Expr might mistakenly
    		// think a conversion between two non-empty interfaces are
    		// identical and set ir.OCONVNOP, instead of ir.OCONVIFACE. To
    		// ensure we update the itab field appropriately, we force it to
    		// ir.OCONVIFACE instead when shape types are involved.
    		//
    		// TODO(mdempsky): Are there other places we might get this wrong?
    		// Should this be moved down into typecheck.{Assign,Convert}op?
    		// This would be a non-issue if itabs were unique for each
    		// *underlying* interface type instead.
    		if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OCONVNOP && n.Type().IsInterface() && !n.Type().IsEmptyInterface() && (n.Type().HasShape() || n.X.Type().HasShape()) {
    			n.SetOp(ir.OCONVIFACE)
    		}
    
    
    		// spec: "If the type is a type parameter, the constant is converted
    		// into a non-constant value of the type parameter."
    		if dstTypeParam && ir.IsConstNode(n) {
    			// Wrap in an OCONVNOP node to ensure result is non-constant.
    			n = Implicit(ir.NewConvExpr(pos, ir.OCONVNOP, n.Type(), n))
    			n.SetTypecheck(1)
    		}
    		return n
    
    // funcInst reads an instantiated function reference, and returns
    // three (possibly nil) expressions related to it:
    //
    // baseFn is always non-nil: it's either a function of the appropriate
    // type already, or it has an extra dictionary parameter as the first
    // parameter.
    //
    // If dictPtr is non-nil, then it's a dictionary argument that must be
    // passed as the first argument to baseFn.
    //
    // If wrapperFn is non-nil, then it's either the same as baseFn (if
    // dictPtr is nil), or it's semantically equivalent to currying baseFn
    // to pass dictPtr. (wrapperFn is nil when dictPtr is an expression
    // that needs to be computed dynamically.)
    //
    // For callers that are creating a call to the returned function, it's
    // best to emit a call to baseFn, and include dictPtr in the arguments
    // list as appropriate.
    //
    // For callers that want to return the function without invoking it,
    // they may return wrapperFn if it's non-nil; but otherwise, they need
    // to create their own wrapper.
    func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
    	// Like in methodExpr, I'm pretty sure this isn't needed.
    	var implicits []*types.Type
    	if r.dict != nil {
    		implicits = r.dict.targs
    	}
    
    	if r.Bool() { // dynamic subdictionary
    		idx := r.Len()
    		info := r.dict.subdicts[idx]
    		explicits := r.p.typListIdx(info.explicits, r.dict)
    
    		baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
    
    		// TODO(mdempsky): Is there a more robust way to get the
    		// dictionary pointer type here?
    		dictPtrType := baseFn.Type().Params().Field(0).Type
    		dictPtr = typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, dictPtrType, r.dictWord(pos, r.dict.subdictsOffset()+idx)))
    
    		return
    	}
    
    	info := r.objInfo()
    	explicits := r.p.typListIdx(info.explicits, r.dict)
    
    	wrapperFn = r.p.objIdx(info.idx, implicits, explicits, false).(*ir.Name)
    	baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
    
    	dictName := r.p.objDictName(info.idx, implicits, explicits)
    	dictPtr = typecheck.Expr(ir.NewAddrExpr(pos, dictName))
    
    	return
    }
    
    func (pr *pkgReader) objDictName(idx pkgbits.Index, implicits, explicits []*types.Type) *ir.Name {
    	rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
    	_, sym := rname.qualifiedIdent()
    	tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
    
    	if tag == pkgbits.ObjStub {
    		assert(!sym.IsBlank())
    		if pri, ok := objReader[sym]; ok {
    			return pri.pr.objDictName(pri.idx, nil, explicits)
    		}
    		base.Fatalf("unresolved stub: %v", sym)
    	}
    
    	dict := pr.objDictIdx(sym, idx, implicits, explicits, false)
    
    	return pr.dictNameOf(dict)
    }
    
    // curry returns a function literal that calls fun with arg0 and
    // (optionally) arg1, accepting additional arguments to the function
    // literal as necessary to satisfy fun's signature.
    //
    // If nilCheck is true and arg0 is an interface value, then it's
    // checked to be non-nil as an initial step at the point of evaluating
    // the function literal itself.
    func (r *reader) curry(pos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1 ir.Node) ir.Node {
    	var captured ir.Nodes
    	captured.Append(fun, arg0)
    	if arg1 != nil {
    		captured.Append(arg1)
    	}
    
    	params, results := syntheticSig(fun.Type())
    	params = params[len(captured)-1:] // skip curried parameters
    	typ := types.NewSignature(types.NoPkg, nil, nil, params, results)
    
    	addBody := func(pos src.XPos, r *reader, captured []ir.Node) {
    		recvs, params := r.syntheticArgs(pos)
    		assert(len(recvs) == 0)
    
    		fun := captured[0]
    
    		var args ir.Nodes
    		args.Append(captured[1:]...)
    		args.Append(params...)
    
    		r.syntheticTailCall(pos, fun, args)
    	}
    
    	return r.syntheticClosure(pos, typ, ifaceHack, captured, addBody)
    }
    
    // methodExprWrap returns a function literal that changes method's
    // first parameter's type to recv, and uses implicits/deref/addr to
    // select the appropriate receiver parameter to pass to method.
    func (r *reader) methodExprWrap(pos src.XPos, recv *types.Type, implicits []int, deref, addr bool, method, dictPtr ir.Node) ir.Node {
    	var captured ir.Nodes
    	captured.Append(method)
    
    	params, results := syntheticSig(method.Type())
    
    	// Change first parameter to recv.
    	params[0].Type = recv
    
    	// If we have a dictionary pointer argument to pass, then omit the
    	// underlying method expression's dictionary parameter from the
    	// returned signature too.
    	if dictPtr != nil {
    		captured.Append(dictPtr)
    		params = append(params[:1], params[2:]...)
    	}
    
    	typ := types.NewSignature(types.NoPkg, nil, nil, params, results)
    
    	addBody := func(pos src.XPos, r *reader, captured []ir.Node) {
    		recvs, args := r.syntheticArgs(pos)
    		assert(len(recvs) == 0)
    
    		fn := captured[0]
    
    		// Rewrite first argument based on implicits/deref/addr.
    		{
    			arg := args[0]
    			for _, ix := range implicits {
    				arg = Implicit(DotField(pos, arg, ix))
    			}
    			if deref {
    				arg = Implicit(Deref(pos, arg.Type().Elem(), arg))
    			} else if addr {
    				arg = Implicit(Addr(pos, arg))
    			}
    			args[0] = arg
    		}
    
    		// Insert dictionary argument, if provided.
    		if dictPtr != nil {
    			newArgs := make([]ir.Node, len(args)+1)
    			newArgs[0] = args[0]
    			newArgs[1] = captured[1]
    			copy(newArgs[2:], args[1:])
    			args = newArgs
    		}
    
    		r.syntheticTailCall(pos, fn, args)
    	}
    
    	return r.syntheticClosure(pos, typ, false, captured, addBody)
    }
    
    // syntheticClosure constructs a synthetic function literal for
    // currying dictionary arguments. pos is the position used for the
    // closure. typ is the function literal's signature type.
    //
    // captures is a list of expressions that need to be evaluated at the
    // point of function literal evaluation and captured by the function
    // literal. If ifaceHack is true and captures[1] is an interface type,
    // it's checked to be non-nil after evaluation.
    //
    // addBody is a callback function to populate the function body. The
    // list of captured values passed back has the captured variables for
    // use within the function literal, corresponding to the expressions
    // in captures.
    func (r *reader) syntheticClosure(pos src.XPos, typ *types.Type, ifaceHack bool, captures ir.Nodes, addBody func(pos src.XPos, r *reader, captured []ir.Node)) ir.Node {
    	// isSafe reports whether n is an expression that we can safely
    	// defer to evaluating inside the closure instead, to avoid storing
    	// them into the closure.
    	//
    	// In practice this is always (and only) the wrappee function.
    	isSafe := func(n ir.Node) bool {
    		if n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PFUNC {
    			return true
    		}
    		if n.Op() == ir.OMETHEXPR {
    			return true
    		}
    
    		return false
    	}
    
    	fn := ir.NewClosureFunc(pos, r.curfn != nil)
    	fn.SetWrapper(true)
    	clo := fn.OClosure
    	ir.NameClosure(clo, r.curfn)
    
    	setType(fn.Nname, typ)
    	typecheck.Func(fn)
    	setType(clo, fn.Type())
    
    	var init ir.Nodes
    	for i, n := range captures {
    		if isSafe(n) {
    			continue // skip capture; can reference directly
    		}
    
    		tmp := r.tempCopy(pos, n, &init)
    		ir.NewClosureVar(pos, fn, tmp)
    
    		// We need to nil check interface receivers at the point of method
    		// value evaluation, ugh.
    		if ifaceHack && i == 1 && n.Type().IsInterface() {
    			check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp))
    			init.Append(typecheck.Stmt(check))
    		}
    	}
    
    	pri := pkgReaderIndex{synthetic: func(pos src.XPos, r *reader) {
    		captured := make([]ir.Node, len(captures))
    		next := 0
    		for i, n := range captures {
    			if isSafe(n) {
    				captured[i] = n
    			} else {
    				captured[i] = r.closureVars[next]
    				next++
    			}
    		}
    		assert(next == len(r.closureVars))
    
    		addBody(pos, r, captured)
    	}}
    	bodyReader[fn] = pri
    	pri.funcBody(fn)
    
    	// TODO(mdempsky): Remove hard-coding of typecheck.Target.
    	return ir.InitExpr(init, ir.UseClosure(clo, typecheck.Target))
    }
    
    // syntheticSig duplicates and returns the params and results lists
    // for sig, but renaming anonymous parameters so they can be assigned
    // ir.Names.
    func syntheticSig(sig *types.Type) (params, results []*types.Field) {
    	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)
    			}
    
    			// TODO(mdempsky): It would be nice to preserve the original
    			// parameter positions here instead, but at least
    			// typecheck.NewMethodType replaces them with base.Pos, making
    			// them useless. Worse, the positions copied from base.Pos may
    			// have inlining contexts, which we definitely don't want here
    			// (e.g., #54625).
    			res[i] = types.NewField(base.AutogeneratedPos, sym, param.Type)
    
    			res[i].SetIsDDD(param.IsDDD())
    		}
    		return res
    	}
    
    	return clone(sig.Params().FieldSlice()), clone(sig.Results().FieldSlice())
    }
    
    
    func (r *reader) optExpr() ir.Node {
    	if r.Bool() {
    		return r.expr()
    	}
    	return nil
    }
    
    
    // methodExpr reads a method expression reference, and returns three
    // (possibly nil) expressions related to it:
    //
    // baseFn is always non-nil: it's either a function of the appropriate
    // type already, or it has an extra dictionary parameter as the second
    // parameter (i.e., immediately after the promoted receiver
    // parameter).
    //
    // If dictPtr is non-nil, then it's a dictionary argument that must be
    // passed as the second argument to baseFn.
    //
    // If wrapperFn is non-nil, then it's either the same as baseFn (if
    // dictPtr is nil), or it's semantically equivalent to currying baseFn
    // to pass dictPtr. (wrapperFn is nil when dictPtr is an expression
    // that needs to be computed dynamically.)
    //
    // For callers that are creating a call to the returned method, it's
    // best to emit a call to baseFn, and include dictPtr in the arguments
    // list as appropriate.
    //
    // For callers that want to return a method expression without
    // invoking it, they may return wrapperFn if it's non-nil; but
    // otherwise, they need to create their own wrapper.
    func (r *reader) methodExpr() (wrapperFn, baseFn, dictPtr ir.Node) {
    	recv := r.typ()
    
    	pos := r.pos()
    	_, sym := r.selector()
    
    	// Signature type to return (i.e., recv prepended to the method's
    	// normal parameters list).
    	sig := typecheck.NewMethodType(sig0, recv)
    
    	if r.Bool() { // type parameter method expression
    		idx := r.Len()
    		word := r.dictWord(pos, r.dict.typeParamMethodExprsOffset()+idx)
    
    		// TODO(mdempsky): If the type parameter was instantiated with an
    		// interface type (i.e., embed.IsInterface()), then we could
    		// return the OMETHEXPR instead and save an indirection.
    
    		// We wrote the method expression's entry point PC into the
    		// dictionary, but for Go `func` values we need to return a
    		// closure (i.e., pointer to a structure with the PC as the first
    		// field). Because method expressions don't have any closure
    		// variables, we pun the dictionary entry as the closure struct.
    		fn := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, sig, ir.NewAddrExpr(pos, word)))
    		return fn, fn, nil
    	}
    
    	// TODO(mdempsky): I'm pretty sure this isn't needed: implicits is
    	// only relevant to locally defined types, but they can't have
    	// (non-promoted) methods.
    	var implicits []*types.Type
    	if r.dict != nil {
    		implicits = r.dict.targs
    	}
    
    	if r.Bool() { // dynamic subdictionary
    		idx := r.Len()
    		info := r.dict.subdicts[idx]
    		explicits := r.p.typListIdx(info.explicits, r.dict)
    
    		shapedObj := r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
    		shapedFn := shapedMethodExpr(pos, shapedObj, sym)
    
    		// TODO(mdempsky): Is there a more robust way to get the
    		// dictionary pointer type here?
    		dictPtrType := shapedFn.Type().Params().Field(1).Type
    		dictPtr := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, dictPtrType, r.dictWord(pos, r.dict.subdictsOffset()+idx)))
    
    		return nil, shapedFn, dictPtr
    	}
    
    	if r.Bool() { // static dictionary
    		info := r.objInfo()
    		explicits := r.p.typListIdx(info.explicits, r.dict)
    
    		shapedObj := r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
    		shapedFn := shapedMethodExpr(pos, shapedObj, sym)
    
    		dict := r.p.objDictName(info.idx, implicits, explicits)
    		dictPtr := typecheck.Expr(ir.NewAddrExpr(pos, dict))
    
    		// Check that dictPtr matches shapedFn's dictionary parameter.
    		if !types.Identical(dictPtr.Type(), shapedFn.Type().Params().Field(1).Type) {
    			base.FatalfAt(pos, "dict %L, but shaped method %L", dict, shapedFn)
    		}
    
    		// For statically known instantiations, we can take advantage of
    		// the stenciled wrapper.
    		base.AssertfAt(!recv.HasShape(), pos, "shaped receiver %v", recv)
    		wrapperFn := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr)
    		base.AssertfAt(types.Identical(sig, wrapperFn.Type()), pos, "wrapper %L does not have type %v", wrapperFn, sig)
    
    		return wrapperFn, shapedFn, dictPtr
    	}
    
    	// Simple method expression; no dictionary needed.
    	base.AssertfAt(!recv.HasShape() || recv.IsInterface(), pos, "shaped receiver %v", recv)
    	fn := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr)
    	return fn, fn, nil
    }
    
    // shapedMethodExpr returns the specified method on the given shaped
    // type.
    func shapedMethodExpr(pos src.XPos, obj *ir.Name, sym *types.Sym) *ir.SelectorExpr {
    	assert(obj.Op() == ir.OTYPE)
    
    	typ := obj.Type()
    	assert(typ.HasShape())
    
    	method := func() *types.Field {
    		for _, method := range typ.Methods().Slice() {
    			if method.Sym == sym {
    				return method
    			}
    		}
    
    		base.FatalfAt(pos, "failed to find method %v in shaped type %v", sym, typ)
    		panic("unreachable")
    	}()
    
    	// Construct an OMETHEXPR node.
    	recv := method.Type.Recv().Type
    	return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr)
    }
    
    
    func (r *reader) multiExpr() []ir.Node {
    	r.Sync(pkgbits.SyncMultiExpr)
    
    
    	if r.Bool() { // N:1
    		pos := r.pos()
    		expr := r.expr()
    
    		results := make([]ir.Node, r.Len())
    		as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr})
    		as.Def = true
    		for i := range results {
    
    			as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
    			as.Lhs.Append(tmp)
    
    			res := ir.Node(tmp)
    			if r.Bool() {
    
    				n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res)
    
    				n.TypeWord, n.SrcRType = r.convRTTI(pos)
    
    			}
    			results[i] = res
    		}
    
    		// TODO(mdempsky): Could use ir.InlinedCallExpr instead?
    		results[0] = ir.InitExpr([]ir.Node{typecheck.Stmt(as)}, results[0])
    		return results
    	}
    
    	// N:N
    
    	exprs := make([]ir.Node, r.Len())
    	if len(exprs) == 0 {
    		return nil
    	}
    	for i := range exprs {
    		exprs[i] = r.expr()
    	}
    	return exprs
    }
    
    
    // temp returns a new autotemp of the specified type.
    func (r *reader) temp(pos src.XPos, typ *types.Type) *ir.Name {
    	// See typecheck.typecheckargs.
    	curfn := r.curfn
    	if curfn == nil {
    		curfn = typecheck.InitTodoFunc
    	}
    
    	return typecheck.TempAt(pos, curfn, typ)
    }
    
    
    // tempCopy declares and returns a new autotemp initialized to the
    // value of expr.
    func (r *reader) tempCopy(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
    	if r.curfn == nil {
    		// Escape analysis doesn't know how to handle package-scope
    		// function literals with free variables (i.e., that capture
    		// temporary variables added to typecheck.InitTodoFunc).
    		//
    		// stencil.go works around this limitation by spilling values to
    		// global variables instead, but that causes the value to stay
    		// alive indefinitely; see go.dev/issue/54343.
    		//
    		// This code path (which implements the same workaround) isn't
    		// actually needed by unified IR, because it creates uses normal
    		// OMETHEXPR/OMETHVALUE nodes when statically-known instantiated
    		// types are used. But it's kept around for now because it's handy
    		// for testing that the generic fallback paths work correctly.
    		base.Fatalf("tempCopy called at package scope")
    
    		tmp := staticinit.StaticName(expr.Type())
    
    		assign := ir.NewAssignStmt(pos, tmp, expr)
    		assign.Def = true
    		tmp.Defn = assign
    
    		typecheck.Target.Decls = append(typecheck.Target.Decls, typecheck.Stmt(assign))
    
    		return tmp
    	}
    
    	tmp := r.temp(pos, expr.Type())
    
    	init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
    
    	assign := ir.NewAssignStmt(pos, tmp, expr)
    	assign.Def = true
    	init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, expr)))
    
    	tmp.Defn = assign
    
    	return tmp
    }
    
    
    func (r *reader) compLit() ir.Node {
    
    	r.Sync(pkgbits.SyncCompLit)
    
    		typ = typ.Elem()
    	}
    	if typ.Kind() == types.TFORW {
    		base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
    	}
    
    	var rtype ir.Node
    	if typ.IsMap() {
    		rtype = r.rtype(pos)
    	}
    
    	isStruct := typ.Kind() == types.TSTRUCT
    
    
    	elems := make([]ir.Node, r.Len())
    
    	for i := range elems {
    		elemp := &elems[i]
    
    		if isStruct {
    
    			sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.Len()), nil)
    
    			*elemp, elemp = sk, &sk.Value