mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
The recent refactoring of SelectorExpr code to helpers broke the handling of MethodExprs when there is an embedded field involved (e.g. test/method7.go, line 48). If there is an embedded field involved, the node op seen in DotMethod() is an ODOT rather than an OTYPE. Also, the receiver type of the result should be the original type, but the new code was using the last type after following the embedding path. Change-Id: I13f7ea6448b03d3e8f974103ee3a027219ca8388 Reviewed-on: https://go-review.googlesource.com/c/go/+/286176 Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Dan Scales <danscales@google.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
235 lines
6.2 KiB
Go
235 lines
6.2 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 (
|
|
"go/constant"
|
|
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/typecheck"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
// Helpers for constructing typed IR nodes.
|
|
//
|
|
// TODO(mdempsky): Move into their own package so they can be easily
|
|
// reused by iimport and frontend optimizations.
|
|
//
|
|
// TODO(mdempsky): Update to consistently return already typechecked
|
|
// results, rather than leaving the caller responsible for using
|
|
// typecheck.Expr or typecheck.Stmt.
|
|
|
|
type ImplicitNode interface {
|
|
ir.Node
|
|
SetImplicit(x bool)
|
|
}
|
|
|
|
// Implicit returns n after marking it as Implicit.
|
|
func Implicit(n ImplicitNode) ImplicitNode {
|
|
n.SetImplicit(true)
|
|
return n
|
|
}
|
|
|
|
// typed returns n after setting its type to typ.
|
|
func typed(typ *types.Type, n ir.Node) ir.Node {
|
|
n.SetType(typ)
|
|
n.SetTypecheck(1)
|
|
return n
|
|
}
|
|
|
|
// Values
|
|
|
|
func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
|
|
return typed(typ, ir.NewBasicLit(pos, val))
|
|
}
|
|
|
|
func Nil(pos src.XPos, typ *types.Type) ir.Node {
|
|
return typed(typ, ir.NewNilExpr(pos))
|
|
}
|
|
|
|
// Expressions
|
|
|
|
func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
|
|
// TODO(mdempsky): Avoid typecheck.Expr. Probably just need to set OPTRLIT when appropriate.
|
|
n := typecheck.Expr(typecheck.NodAddrAt(pos, x)).(*ir.AddrExpr)
|
|
typed(types.NewPtr(x.Type()), n)
|
|
return n
|
|
}
|
|
|
|
func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
|
|
return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
|
|
}
|
|
|
|
func Binary(pos src.XPos, op ir.Op, x, y ir.Node) ir.Node {
|
|
switch op {
|
|
case ir.OANDAND, ir.OOROR:
|
|
return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
|
|
case ir.OADD:
|
|
if x.Type().IsString() {
|
|
// TODO(mdempsky): Construct OADDSTR directly.
|
|
return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
|
|
}
|
|
fallthrough
|
|
default:
|
|
return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y))
|
|
}
|
|
}
|
|
|
|
func Call(pos src.XPos, fun ir.Node, args []ir.Node, dots bool) ir.Node {
|
|
// TODO(mdempsky): This should not be so difficult.
|
|
|
|
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
|
|
n.IsDDD = dots
|
|
|
|
// Actually a type conversion.
|
|
if fun.Op() == ir.OTYPE {
|
|
return typecheck.Expr(n)
|
|
}
|
|
|
|
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
|
|
switch fun.BuiltinOp {
|
|
case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN:
|
|
return typecheck.Stmt(n)
|
|
default:
|
|
return typecheck.Expr(n)
|
|
}
|
|
}
|
|
|
|
// Add information, now that we know that fun is actually being called.
|
|
switch fun := fun.(type) {
|
|
case *ir.ClosureExpr:
|
|
fun.Func.SetClosureCalled(true)
|
|
case *ir.SelectorExpr:
|
|
if fun.Op() == ir.OCALLPART {
|
|
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)
|
|
}
|
|
}
|
|
|
|
typecheck.Call(n)
|
|
return n
|
|
}
|
|
|
|
func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
|
|
n := ir.NewBinaryExpr(pos, op, x, y)
|
|
if !types.Identical(x.Type(), y.Type()) {
|
|
// TODO(mdempsky): Handle subtleties of constructing mixed-typed comparisons.
|
|
n = typecheck.Expr(n).(*ir.BinaryExpr)
|
|
}
|
|
return typed(typ, n)
|
|
}
|
|
|
|
func Deref(pos src.XPos, x ir.Node) *ir.StarExpr {
|
|
n := ir.NewStarExpr(pos, x)
|
|
typed(x.Type().Elem(), n)
|
|
return n
|
|
}
|
|
|
|
func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
|
op, typ := ir.ODOT, x.Type()
|
|
if typ.IsPtr() {
|
|
op, typ = ir.ODOTPTR, typ.Elem()
|
|
}
|
|
if !typ.IsStruct() {
|
|
base.FatalfAt(pos, "DotField of non-struct: %L", x)
|
|
}
|
|
|
|
// TODO(mdempsky): This is the backend's responsibility.
|
|
types.CalcSize(typ)
|
|
|
|
field := typ.Field(index)
|
|
return dot(pos, field.Type, op, x, field)
|
|
}
|
|
|
|
func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
|
method := method(x.Type(), index)
|
|
|
|
// Method value.
|
|
typ := typecheck.NewMethodType(method.Type, nil)
|
|
return dot(pos, typ, ir.OCALLPART, x, method)
|
|
}
|
|
|
|
// MethodExpr returns a OMETHEXPR node with the indicated index into the methods
|
|
// of typ. The receiver type is set from recv, which is different from typ if the
|
|
// method was accessed via embedded fields. Similarly, the X value of the
|
|
// ir.SelectorExpr is recv, the original OTYPE node before passing through the
|
|
// embedded fields.
|
|
func MethodExpr(pos src.XPos, recv ir.Node, embed *types.Type, index int) *ir.SelectorExpr {
|
|
method := method(embed, index)
|
|
typ := typecheck.NewMethodType(method.Type, recv.Type())
|
|
// The method expression T.m requires a wrapper when T
|
|
// is different from m's declared receiver type. We
|
|
// normally generate these wrappers while writing out
|
|
// runtime type descriptors, which is always done for
|
|
// types declared at package scope. However, we need
|
|
// to make sure to generate wrappers for anonymous
|
|
// receiver types too.
|
|
if recv.Sym() == nil {
|
|
typecheck.NeedRuntimeType(recv.Type())
|
|
}
|
|
return dot(pos, typ, ir.OMETHEXPR, recv, method)
|
|
}
|
|
|
|
func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
|
|
n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
|
|
n.Selection = selection
|
|
typed(typ, n)
|
|
return n
|
|
}
|
|
|
|
// TODO(mdempsky): Move to package types.
|
|
func method(typ *types.Type, index int) *types.Field {
|
|
if typ.IsInterface() {
|
|
return typ.Field(index)
|
|
}
|
|
return types.ReceiverBaseType(typ).Methods().Index(index)
|
|
}
|
|
|
|
func Index(pos src.XPos, x, index ir.Node) ir.Node {
|
|
// TODO(mdempsky): Avoid typecheck.Expr.
|
|
return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
|
|
}
|
|
|
|
func Slice(pos src.XPos, x, low, high, max ir.Node) ir.Node {
|
|
op := ir.OSLICE
|
|
if max != nil {
|
|
op = ir.OSLICE3
|
|
}
|
|
// TODO(mdempsky): Avoid typecheck.Expr.
|
|
return typecheck.Expr(ir.NewSliceExpr(pos, op, x, low, high, max))
|
|
}
|
|
|
|
func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
|
|
switch op {
|
|
case ir.OADDR:
|
|
return Addr(pos, x)
|
|
case ir.ODEREF:
|
|
return Deref(pos, x)
|
|
}
|
|
|
|
typ := x.Type()
|
|
if op == ir.ORECV {
|
|
typ = typ.Elem()
|
|
}
|
|
return typed(typ, ir.NewUnaryExpr(pos, op, x))
|
|
}
|
|
|
|
// Statements
|
|
|
|
var one = constant.MakeInt64(1)
|
|
|
|
func IncDec(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
|
|
x = typecheck.AssignExpr(x)
|
|
return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
|
|
}
|