mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
In preparation for encoding it in a more efficient way. Change-Id: I299dd2befc3d07107a1b7b49225bbb9f2e48a343 Reviewed-on: https://go-review.googlesource.com/c/go/+/432896 Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Keith Randall <khr@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
474 lines
13 KiB
Go
474 lines
13 KiB
Go
// 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 noder
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/syntax"
|
|
"cmd/compile/internal/typecheck"
|
|
"cmd/compile/internal/types"
|
|
"cmd/compile/internal/types2"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
func (g *irgen) expr(expr syntax.Expr) ir.Node {
|
|
expr = unparen(expr) // skip parens; unneeded after parse+typecheck
|
|
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" {
|
|
return ir.BlankNode
|
|
}
|
|
|
|
tv := g.typeAndValue(expr)
|
|
switch {
|
|
case tv.IsBuiltin():
|
|
// Qualified builtins, such as unsafe.Add and unsafe.Slice.
|
|
if expr, ok := expr.(*syntax.SelectorExpr); ok {
|
|
if name, ok := expr.X.(*syntax.Name); ok {
|
|
if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
|
|
return g.use(expr.Sel)
|
|
}
|
|
}
|
|
}
|
|
return g.use(expr.(*syntax.Name))
|
|
case tv.IsType():
|
|
return ir.TypeNode(g.typ(tv.Type))
|
|
case tv.IsValue(), tv.IsVoid():
|
|
// ok
|
|
default:
|
|
base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
|
|
}
|
|
|
|
base.Assert(g.exprStmtOK)
|
|
|
|
typ := idealType(tv)
|
|
if typ == nil {
|
|
base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", tv.Type)
|
|
}
|
|
|
|
// Constant expression.
|
|
if tv.Value != nil {
|
|
typ := g.typ(typ)
|
|
value := FixValue(typ, tv.Value)
|
|
return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr))
|
|
}
|
|
|
|
n := g.expr0(typ, expr)
|
|
if n.Typecheck() != 1 && n.Typecheck() != 3 {
|
|
base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n)
|
|
}
|
|
if n.Op() != ir.OFUNCINST && !g.match(n.Type(), typ, tv.HasOk()) {
|
|
base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ)
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
|
|
pos := g.pos(expr)
|
|
assert(pos.IsKnown())
|
|
|
|
// Set base.Pos for transformation code that still uses base.Pos, rather than
|
|
// the pos of the node being converted.
|
|
base.Pos = pos
|
|
|
|
switch expr := expr.(type) {
|
|
case *syntax.Name:
|
|
if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil {
|
|
return Nil(pos, g.typ(typ))
|
|
}
|
|
return g.use(expr)
|
|
|
|
case *syntax.CompositeLit:
|
|
return g.compLit(typ, expr)
|
|
|
|
case *syntax.FuncLit:
|
|
return g.funcLit(typ, expr)
|
|
|
|
case *syntax.AssertExpr:
|
|
return Assert(pos, g.expr(expr.X), g.typeExpr(expr.Type))
|
|
|
|
case *syntax.CallExpr:
|
|
fun := g.expr(expr.Fun)
|
|
return g.callExpr(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots)
|
|
|
|
case *syntax.IndexExpr:
|
|
args := unpackListExpr(expr.Index)
|
|
if len(args) == 1 {
|
|
tv := g.typeAndValue(args[0])
|
|
if tv.IsValue() {
|
|
// This is just a normal index expression
|
|
n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0]))
|
|
if !g.delayTransform() {
|
|
// transformIndex will modify n.Type() for OINDEXMAP.
|
|
transformIndex(n)
|
|
}
|
|
return n
|
|
}
|
|
}
|
|
|
|
// expr.Index is a list of type args, so we ignore it, since types2 has
|
|
// already provided this info with the Info.Instances map.
|
|
return g.expr(expr.X)
|
|
|
|
case *syntax.SelectorExpr:
|
|
// Qualified identifier.
|
|
if name, ok := expr.X.(*syntax.Name); ok {
|
|
if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
|
|
return g.use(expr.Sel)
|
|
}
|
|
}
|
|
return g.selectorExpr(pos, typ, expr)
|
|
|
|
case *syntax.SliceExpr:
|
|
n := Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
|
|
if !g.delayTransform() {
|
|
transformSlice(n)
|
|
}
|
|
return n
|
|
|
|
case *syntax.Operation:
|
|
if expr.Y == nil {
|
|
n := Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X))
|
|
if n.Op() == ir.OADDR && !g.delayTransform() {
|
|
transformAddr(n.(*ir.AddrExpr))
|
|
}
|
|
return n
|
|
}
|
|
switch op := g.op(expr.Op, binOps[:]); op {
|
|
case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
|
|
n := Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y))
|
|
if !g.delayTransform() {
|
|
transformCompare(n)
|
|
}
|
|
return n
|
|
case ir.OANDAND, ir.OOROR:
|
|
x := g.expr(expr.X)
|
|
y := g.expr(expr.Y)
|
|
return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
|
|
default:
|
|
n := Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y))
|
|
if op == ir.OADD && !g.delayTransform() {
|
|
return transformAdd(n)
|
|
}
|
|
return n
|
|
}
|
|
|
|
default:
|
|
g.unhandled("expression", expr)
|
|
panic("unreachable")
|
|
}
|
|
}
|
|
|
|
// substType does a normal type substition, but tparams is in the form of a field
|
|
// list, and targs is in terms of a slice of type nodes. substType records any newly
|
|
// instantiated types into g.instTypeList.
|
|
func (g *irgen) substType(typ *types.Type, tparams *types.Type, targs []ir.Ntype) *types.Type {
|
|
fields := tparams.FieldSlice()
|
|
tparams1 := make([]*types.Type, len(fields))
|
|
for i, f := range fields {
|
|
tparams1[i] = f.Type
|
|
}
|
|
targs1 := make([]*types.Type, len(targs))
|
|
for i, n := range targs {
|
|
targs1[i] = n.Type()
|
|
}
|
|
ts := typecheck.Tsubster{
|
|
Tparams: tparams1,
|
|
Targs: targs1,
|
|
}
|
|
newt := ts.Typ(typ)
|
|
return newt
|
|
}
|
|
|
|
// callExpr creates a call expression (which might be a type conversion, built-in
|
|
// call, or a regular call) and does standard transforms, unless we are in a generic
|
|
// function.
|
|
func (g *irgen) callExpr(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node {
|
|
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
|
|
n.IsDDD = dots
|
|
typed(typ, n)
|
|
|
|
if fun.Op() == ir.OTYPE {
|
|
// Actually a type conversion, not a function call.
|
|
if !g.delayTransform() {
|
|
return transformConvCall(n)
|
|
}
|
|
return n
|
|
}
|
|
|
|
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
|
|
if !g.delayTransform() {
|
|
return transformBuiltin(n)
|
|
}
|
|
return n
|
|
}
|
|
|
|
// Add information, now that we know that fun is actually being called.
|
|
switch fun := fun.(type) {
|
|
case *ir.SelectorExpr:
|
|
if fun.Op() == ir.OMETHVALUE {
|
|
op := ir.ODOTMETH
|
|
if fun.X.Type().IsInterface() {
|
|
op = ir.ODOTINTER
|
|
}
|
|
fun.SetOp(op)
|
|
// Set the type to include the receiver, since that's what
|
|
// later parts of the compiler expect
|
|
fun.SetType(fun.Selection.Type)
|
|
}
|
|
}
|
|
|
|
// A function instantiation (even if fully concrete) shouldn't be
|
|
// transformed yet, because we need to add the dictionary during the
|
|
// transformation.
|
|
if fun.Op() != ir.OFUNCINST && !g.delayTransform() {
|
|
transformCall(n)
|
|
}
|
|
return n
|
|
}
|
|
|
|
// selectorExpr resolves the choice of ODOT, ODOTPTR, OMETHVALUE (eventually
|
|
// ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather
|
|
// than in typecheck.go.
|
|
func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node {
|
|
x := g.expr(expr.X)
|
|
if x.Type().HasTParam() {
|
|
// Leave a method call on a type param as an OXDOT, since it can
|
|
// only be fully transformed once it has an instantiated type.
|
|
n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value))
|
|
typed(g.typ(typ), n)
|
|
return n
|
|
}
|
|
|
|
selinfo := g.info.Selections[expr]
|
|
// Everything up to the last selection is an implicit embedded field access,
|
|
// and the last selection is determined by selinfo.Kind().
|
|
index := selinfo.Index()
|
|
embeds, last := index[:len(index)-1], index[len(index)-1]
|
|
|
|
origx := x
|
|
for _, ix := range embeds {
|
|
x = Implicit(DotField(pos, x, ix))
|
|
}
|
|
|
|
kind := selinfo.Kind()
|
|
if kind == types2.FieldVal {
|
|
return DotField(pos, x, last)
|
|
}
|
|
|
|
var n ir.Node
|
|
method2 := selinfo.Obj().(*types2.Func)
|
|
|
|
if kind == types2.MethodExpr {
|
|
// OMETHEXPR is unusual in using directly the node and type of the
|
|
// original OTYPE node (origx) before passing through embedded
|
|
// fields, even though the method is selected from the type
|
|
// (x.Type()) reached after following the embedded fields. We will
|
|
// actually drop any ODOT nodes we created due to the embedded
|
|
// fields.
|
|
n = MethodExpr(pos, origx, x.Type(), last)
|
|
} else {
|
|
// Add implicit addr/deref for method values, if needed.
|
|
if x.Type().IsInterface() {
|
|
n = DotMethod(pos, x, last)
|
|
} else {
|
|
recvType2 := method2.Type().(*types2.Signature).Recv().Type()
|
|
_, wantPtr := recvType2.(*types2.Pointer)
|
|
havePtr := x.Type().IsPtr()
|
|
|
|
if havePtr != wantPtr {
|
|
if havePtr {
|
|
x = Implicit(Deref(pos, x.Type().Elem(), x))
|
|
} else {
|
|
x = Implicit(Addr(pos, x))
|
|
}
|
|
}
|
|
recvType2Base := recvType2
|
|
if wantPtr {
|
|
recvType2Base = types2.AsPointer(recvType2).Elem()
|
|
}
|
|
if recvType2Base.(*types2.Named).TypeParams().Len() > 0 {
|
|
// recvType2 is the original generic type that is
|
|
// instantiated for this method call.
|
|
// selinfo.Recv() is the instantiated type
|
|
recvType2 = recvType2Base
|
|
recvTypeSym := g.pkg(method2.Pkg()).Lookup(recvType2.(*types2.Named).Obj().Name())
|
|
recvType := recvTypeSym.Def.(*ir.Name).Type()
|
|
// method is the generic method associated with
|
|
// the base generic type. The instantiated type may not
|
|
// have method bodies filled in, if it was imported.
|
|
method := recvType.Methods().Index(last).Nname.(*ir.Name)
|
|
n = ir.NewSelectorExpr(pos, ir.OMETHVALUE, x, typecheck.Lookup(expr.Sel.Value))
|
|
n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type())
|
|
n.(*ir.SelectorExpr).Selection.Nname = method
|
|
typed(method.Type(), n)
|
|
|
|
xt := deref(x.Type())
|
|
targs := make([]ir.Ntype, len(xt.RParams()))
|
|
for i := range targs {
|
|
targs[i] = ir.TypeNode(xt.RParams()[i])
|
|
}
|
|
|
|
// Create function instantiation with the type
|
|
// args for the receiver type for the method call.
|
|
n = ir.NewInstExpr(pos, ir.OFUNCINST, n, targs)
|
|
typed(g.typ(typ), n)
|
|
return n
|
|
}
|
|
|
|
if !g.match(x.Type(), recvType2, false) {
|
|
base.FatalfAt(pos, "expected %L to have type %v", x, recvType2)
|
|
} else {
|
|
n = DotMethod(pos, x, last)
|
|
}
|
|
}
|
|
}
|
|
if have, want := n.Sym(), g.selector(method2); have != want {
|
|
base.FatalfAt(pos, "bad Sym: have %v, want %v", have, want)
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
|
|
return g.exprs(unpackListExpr(expr))
|
|
}
|
|
|
|
func unpackListExpr(expr syntax.Expr) []syntax.Expr {
|
|
switch expr := expr.(type) {
|
|
case nil:
|
|
return nil
|
|
case *syntax.ListExpr:
|
|
return expr.ElemList
|
|
default:
|
|
return []syntax.Expr{expr}
|
|
}
|
|
}
|
|
|
|
func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node {
|
|
nodes := make([]ir.Node, len(exprs))
|
|
for i, expr := range exprs {
|
|
nodes[i] = g.expr(expr)
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
|
|
if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
|
|
n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
|
|
n.SetOp(ir.OPTRLIT)
|
|
return typed(g.typ(typ), n)
|
|
}
|
|
|
|
_, isStruct := types2.CoreType(typ).(*types2.Struct)
|
|
|
|
exprs := make([]ir.Node, len(lit.ElemList))
|
|
for i, elem := range lit.ElemList {
|
|
switch elem := elem.(type) {
|
|
case *syntax.KeyValueExpr:
|
|
var key ir.Node
|
|
if isStruct {
|
|
key = ir.NewIdent(g.pos(elem.Key), g.name(elem.Key.(*syntax.Name)))
|
|
} else {
|
|
key = g.expr(elem.Key)
|
|
}
|
|
value := wrapname(g.pos(elem.Value), g.expr(elem.Value))
|
|
if value.Op() == ir.OPAREN {
|
|
// Make sure any PAREN node added by wrapper has a type
|
|
typed(value.(*ir.ParenExpr).X.Type(), value)
|
|
}
|
|
exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value)
|
|
default:
|
|
exprs[i] = wrapname(g.pos(elem), g.expr(elem))
|
|
if exprs[i].Op() == ir.OPAREN {
|
|
// Make sure any PAREN node added by wrapper has a type
|
|
typed(exprs[i].(*ir.ParenExpr).X.Type(), exprs[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
n := ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, nil, exprs)
|
|
typed(g.typ(typ), n)
|
|
var r ir.Node = n
|
|
if !g.delayTransform() {
|
|
r = transformCompLit(n)
|
|
}
|
|
return r
|
|
}
|
|
|
|
func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
|
|
fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc != nil)
|
|
ir.NameClosure(fn.OClosure, ir.CurFunc)
|
|
|
|
typ := g.typ(typ2)
|
|
typed(typ, fn.Nname)
|
|
typed(typ, fn.OClosure)
|
|
fn.SetTypecheck(1)
|
|
|
|
g.funcBody(fn, nil, expr.Type, expr.Body)
|
|
|
|
ir.FinishCaptureNames(fn.Pos(), ir.CurFunc, fn)
|
|
|
|
// TODO(mdempsky): ir.CaptureName should probably handle
|
|
// copying these fields from the canonical variable.
|
|
for _, cv := range fn.ClosureVars {
|
|
cv.SetType(cv.Canonical().Type())
|
|
cv.SetTypecheck(1)
|
|
}
|
|
|
|
if g.topFuncIsGeneric {
|
|
// Don't add any closure inside a generic function/method to the
|
|
// g.target.Decls list, even though it may not be generic itself.
|
|
// See issue #47514.
|
|
return ir.UseClosure(fn.OClosure, nil)
|
|
} else {
|
|
return ir.UseClosure(fn.OClosure, g.target)
|
|
}
|
|
}
|
|
|
|
func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
|
|
n := g.expr(typ)
|
|
if n.Op() != ir.OTYPE {
|
|
base.FatalfAt(g.pos(typ), "expected type: %L", n)
|
|
}
|
|
return n.Type()
|
|
}
|
|
|
|
// constExprOp returns an ir.Op that represents the outermost
|
|
// operation of the given constant expression. It's intended for use
|
|
// with ir.RawOrigExpr.
|
|
func constExprOp(expr syntax.Expr) ir.Op {
|
|
switch expr := expr.(type) {
|
|
default:
|
|
panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr))
|
|
|
|
case *syntax.BasicLit:
|
|
return ir.OLITERAL
|
|
case *syntax.Name, *syntax.SelectorExpr:
|
|
return ir.ONAME
|
|
case *syntax.CallExpr:
|
|
return ir.OCALL
|
|
case *syntax.Operation:
|
|
if expr.Y == nil {
|
|
return unOps[expr.Op]
|
|
}
|
|
return binOps[expr.Op]
|
|
}
|
|
}
|
|
|
|
func unparen(expr syntax.Expr) syntax.Expr {
|
|
for {
|
|
paren, ok := expr.(*syntax.ParenExpr)
|
|
if !ok {
|
|
return expr
|
|
}
|
|
expr = paren.X
|
|
}
|
|
}
|