mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: create/use noder2 transform functions for more node types
Pull out the transformation part of the typechecking functions for:
- assignment statements
- return statements
- send statements
- select statements
- type conversions
- normal function/method calls
- index operations
The transform functions are like the original typechecking functions,
but with all code removed related to:
- Detecting compile-time errors (already done by types2)
- Setting the actual type of existing nodes (already done based on
info from types2)
- Dealing with untyped constants
Moved all the transformation functions to a separate file, transform.go.
Continuing with the same pattern, we delay transforming a node if it has
any type params in its args, marking it with a typecheck flag of 3, and
do the actual transformation during stenciling.
Assignment statements are tricky, since their transformation must be
delayed if any of the left or right-hands-sides are delayed.
Still to do are:
- selector expressions (OXDOT)
- composite literal expressions (OCOMPLIT)
- builtin function calls
Change-Id: Ie608cadbbc69b40db0067a5536cf707dd974aacc
Reviewed-on: https://go-review.googlesource.com/c/go/+/304049
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
29ed12d4c7
commit
e7e0995cba
7 changed files with 650 additions and 157 deletions
|
|
@ -67,31 +67,6 @@ func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
|
||||||
return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
|
return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformAdd transforms an addition operation (currently just addition of
|
|
||||||
// strings). Equivalent to the "binary operators" case in typecheck.typecheck1.
|
|
||||||
func transformAdd(n *ir.BinaryExpr) ir.Node {
|
|
||||||
l := n.X
|
|
||||||
if l.Type().IsString() {
|
|
||||||
var add *ir.AddStringExpr
|
|
||||||
if l.Op() == ir.OADDSTR {
|
|
||||||
add = l.(*ir.AddStringExpr)
|
|
||||||
add.SetPos(n.Pos())
|
|
||||||
} else {
|
|
||||||
add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
|
|
||||||
}
|
|
||||||
r := n.Y
|
|
||||||
if r.Op() == ir.OADDSTR {
|
|
||||||
r := r.(*ir.AddStringExpr)
|
|
||||||
add.List.Append(r.List.Take()...)
|
|
||||||
} else {
|
|
||||||
add.List.Append(r)
|
|
||||||
}
|
|
||||||
add.SetType(l.Type())
|
|
||||||
return add
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node {
|
func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node {
|
||||||
switch op {
|
switch op {
|
||||||
case ir.OANDAND, ir.OOROR:
|
case ir.OANDAND, ir.OOROR:
|
||||||
|
|
@ -124,7 +99,9 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
|
||||||
// the type.
|
// the type.
|
||||||
return typed(typ, n)
|
return typed(typ, n)
|
||||||
}
|
}
|
||||||
return typecheck.Expr(n)
|
n1 := transformConvCall(n)
|
||||||
|
n1.SetTypecheck(1)
|
||||||
|
return n1
|
||||||
}
|
}
|
||||||
|
|
||||||
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
|
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
|
||||||
|
|
@ -181,6 +158,11 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.Use = ir.CallUseExpr
|
||||||
|
if fun.Type().NumResults() == 0 {
|
||||||
|
n.Use = ir.CallUseStmt
|
||||||
|
}
|
||||||
|
|
||||||
if fun.Op() == ir.OXDOT {
|
if fun.Op() == ir.OXDOT {
|
||||||
if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
|
if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
|
||||||
base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
|
base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
|
||||||
|
|
@ -192,63 +174,18 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
if fun.Op() != ir.OFUNCINST {
|
if fun.Op() != ir.OFUNCINST {
|
||||||
// If no type params, do normal typechecking, since we're
|
// If no type params, do the normal call transformations. This
|
||||||
// still missing some things done by tcCall (mainly
|
// will convert OCALL to OCALLFUNC.
|
||||||
// typecheckaste/assignconvfn - implementing assignability of args
|
transformCall(n)
|
||||||
// to params). This will convert OCALL to OCALLFUNC.
|
typed(typ, n)
|
||||||
typecheck.Call(n)
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave the op as OCALL, which indicates the call still needs typechecking.
|
// Leave the op as OCALL, which indicates the call still needs typechecking.
|
||||||
n.Use = ir.CallUseExpr
|
|
||||||
if fun.Type().NumResults() == 0 {
|
|
||||||
n.Use = ir.CallUseStmt
|
|
||||||
}
|
|
||||||
typed(typ, n)
|
typed(typ, n)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformCompare transforms a compare operation (currently just equals/not
|
|
||||||
// equals). Equivalent to the "comparison operators" case in
|
|
||||||
// typecheck.typecheck1, including tcArith.
|
|
||||||
func transformCompare(n *ir.BinaryExpr) {
|
|
||||||
if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) {
|
|
||||||
// Comparison is okay as long as one side is assignable to the
|
|
||||||
// other. The only allowed case where the conversion is not CONVNOP is
|
|
||||||
// "concrete == interface". In that case, check comparability of
|
|
||||||
// the concrete type. The conversion allocates, so only do it if
|
|
||||||
// the concrete type is huge.
|
|
||||||
l, r := n.X, n.Y
|
|
||||||
lt, rt := l.Type(), r.Type()
|
|
||||||
converted := false
|
|
||||||
if rt.Kind() != types.TBLANK {
|
|
||||||
aop, _ := typecheck.Assignop(lt, rt)
|
|
||||||
if aop != ir.OXXX {
|
|
||||||
types.CalcSize(lt)
|
|
||||||
if rt.IsInterface() == lt.IsInterface() || lt.Width >= 1<<16 {
|
|
||||||
l = ir.NewConvExpr(base.Pos, aop, rt, l)
|
|
||||||
l.SetTypecheck(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
converted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !converted && lt.Kind() != types.TBLANK {
|
|
||||||
aop, _ := typecheck.Assignop(rt, lt)
|
|
||||||
if aop != ir.OXXX {
|
|
||||||
types.CalcSize(rt)
|
|
||||||
if rt.IsInterface() == lt.IsInterface() || rt.Width >= 1<<16 {
|
|
||||||
r = ir.NewConvExpr(base.Pos, aop, lt, r)
|
|
||||||
r.SetTypecheck(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n.X, n.Y = l, r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
|
func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
|
||||||
n := ir.NewBinaryExpr(pos, op, x, y)
|
n := ir.NewBinaryExpr(pos, op, x, y)
|
||||||
if x.Type().HasTParam() || y.Type().HasTParam() {
|
if x.Type().HasTParam() || y.Type().HasTParam() {
|
||||||
|
|
@ -330,38 +267,16 @@ func method(typ *types.Type, index int) *types.Field {
|
||||||
|
|
||||||
func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node {
|
func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node {
|
||||||
n := ir.NewIndexExpr(pos, x, index)
|
n := ir.NewIndexExpr(pos, x, index)
|
||||||
// TODO(danscales): Temporary fix. Need to separate out the
|
if x.Type().HasTParam() {
|
||||||
// transformations done by the old typechecker (in tcIndex()), to be
|
// transformIndex needs to know exact type
|
||||||
// called here or after stenciling.
|
n.SetType(typ)
|
||||||
if x.Type().HasTParam() && x.Type().Kind() != types.TMAP &&
|
n.SetTypecheck(3)
|
||||||
x.Type().Kind() != types.TSLICE && x.Type().Kind() != types.TARRAY {
|
|
||||||
// Old typechecker will complain if arg is not obviously a slice/array/map.
|
|
||||||
typed(typ, n)
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
return typecheck.Expr(n)
|
typed(typ, n)
|
||||||
}
|
// transformIndex will modify n.Type() for OINDEXMAP.
|
||||||
|
transformIndex(n)
|
||||||
// transformSlice transforms a slice operation. Equivalent to typecheck.tcSlice.
|
return n
|
||||||
func transformSlice(n *ir.SliceExpr) {
|
|
||||||
l := n.X
|
|
||||||
if l.Type().IsArray() {
|
|
||||||
addr := typecheck.NodAddr(n.X)
|
|
||||||
addr.SetImplicit(true)
|
|
||||||
typed(types.NewPtr(n.X.Type()), addr)
|
|
||||||
n.X = addr
|
|
||||||
l = addr
|
|
||||||
}
|
|
||||||
t := l.Type()
|
|
||||||
if t.IsString() {
|
|
||||||
n.SetOp(ir.OSLICESTR)
|
|
||||||
} else if t.IsPtr() && t.Elem().IsArray() {
|
|
||||||
if n.Op().IsSlice3() {
|
|
||||||
n.SetOp(ir.OSLICE3ARR)
|
|
||||||
} else {
|
|
||||||
n.SetOp(ir.OSLICEARR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node {
|
func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node {
|
||||||
|
|
@ -399,7 +314,7 @@ func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
|
||||||
|
|
||||||
var one = constant.MakeInt64(1)
|
var one = constant.MakeInt64(1)
|
||||||
|
|
||||||
func IncDec(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
|
func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
|
||||||
x = typecheck.AssignExpr(x)
|
assert(x.Type() != nil)
|
||||||
return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
|
return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,9 @@ func (g *irgen) stencil() {
|
||||||
copy(withRecv[1:], call.Args)
|
copy(withRecv[1:], call.Args)
|
||||||
call.Args = withRecv
|
call.Args = withRecv
|
||||||
}
|
}
|
||||||
// Do the typechecking of the Call now, which changes OCALL
|
// Transform the Call now, which changes OCALL
|
||||||
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
||||||
call.SetTypecheck(0)
|
transformCall(call)
|
||||||
typecheck.Call(call)
|
|
||||||
modified = true
|
modified = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -372,17 +371,37 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
// their instantiated type was known.
|
// their instantiated type was known.
|
||||||
if typecheck.IsCmp(x.Op()) {
|
if typecheck.IsCmp(x.Op()) {
|
||||||
transformCompare(m.(*ir.BinaryExpr))
|
transformCompare(m.(*ir.BinaryExpr))
|
||||||
m.SetTypecheck(1)
|
|
||||||
} else if x.Op() == ir.OSLICE || x.Op() == ir.OSLICE3 {
|
|
||||||
transformSlice(m.(*ir.SliceExpr))
|
|
||||||
m.SetTypecheck(1)
|
|
||||||
} else if x.Op() == ir.OADD {
|
|
||||||
m = transformAdd(m.(*ir.BinaryExpr))
|
|
||||||
m.SetTypecheck(1)
|
|
||||||
} else {
|
} else {
|
||||||
|
switch x.Op() {
|
||||||
|
case ir.OSLICE:
|
||||||
|
case ir.OSLICE3:
|
||||||
|
transformSlice(m.(*ir.SliceExpr))
|
||||||
|
|
||||||
|
case ir.OADD:
|
||||||
|
m = transformAdd(m.(*ir.BinaryExpr))
|
||||||
|
|
||||||
|
case ir.OINDEX:
|
||||||
|
transformIndex(m.(*ir.IndexExpr))
|
||||||
|
|
||||||
|
case ir.OAS2:
|
||||||
|
as2 := m.(*ir.AssignListStmt)
|
||||||
|
transformAssign(as2, as2.Lhs, as2.Rhs)
|
||||||
|
|
||||||
|
case ir.OAS:
|
||||||
|
as := m.(*ir.AssignStmt)
|
||||||
|
lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
|
||||||
|
transformAssign(as, lhs, rhs)
|
||||||
|
|
||||||
|
case ir.OASOP:
|
||||||
|
as := m.(*ir.AssignOpStmt)
|
||||||
|
transformCheckAssign(as, as.X)
|
||||||
|
|
||||||
|
default:
|
||||||
base.Fatalf("Unexpected node with Typecheck() == 3")
|
base.Fatalf("Unexpected node with Typecheck() == 3")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m.SetTypecheck(1)
|
||||||
|
}
|
||||||
|
|
||||||
switch x.Op() {
|
switch x.Op() {
|
||||||
case ir.OLITERAL:
|
case ir.OLITERAL:
|
||||||
|
|
@ -415,26 +434,25 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
case ir.OCALL:
|
case ir.OCALL:
|
||||||
call := m.(*ir.CallExpr)
|
call := m.(*ir.CallExpr)
|
||||||
if call.X.Op() == ir.OTYPE {
|
if call.X.Op() == ir.OTYPE {
|
||||||
// Do typechecking on a conversion, now that we
|
// Transform the conversion, now that we know the
|
||||||
// know the type argument.
|
// type argument.
|
||||||
m.SetTypecheck(0)
|
m = transformConvCall(m.(*ir.CallExpr))
|
||||||
m = typecheck.Expr(m)
|
m.SetTypecheck(1)
|
||||||
} else if call.X.Op() == ir.OCALLPART {
|
} else if call.X.Op() == ir.OCALLPART {
|
||||||
// Redo the typechecking, now that we know the method
|
// Redo the typechecking of OXDOT, now that we
|
||||||
// value is being called.
|
// know the method value is being called. Then
|
||||||
|
// transform the call.
|
||||||
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
||||||
call.X.SetTypecheck(0)
|
call.X.SetTypecheck(0)
|
||||||
call.X.SetType(nil)
|
call.X.SetType(nil)
|
||||||
typecheck.Callee(call.X)
|
typecheck.Callee(call.X)
|
||||||
call.SetTypecheck(0)
|
transformCall(call)
|
||||||
typecheck.Call(call)
|
|
||||||
} else if call.X.Op() == ir.ODOT || call.X.Op() == ir.ODOTPTR {
|
} else if call.X.Op() == ir.ODOT || call.X.Op() == ir.ODOTPTR {
|
||||||
// An OXDOT for a generic receiver was resolved to
|
// An OXDOT for a generic receiver was resolved to
|
||||||
// an access to a field which has a function
|
// an access to a field which has a function
|
||||||
// value. Typecheck the call to that function, now
|
// value. Transform the call to that function, now
|
||||||
// that the OXDOT was resolved.
|
// that the OXDOT was resolved.
|
||||||
call.SetTypecheck(0)
|
transformCall(call)
|
||||||
typecheck.Call(call)
|
|
||||||
} else if name := call.X.Name(); name != nil {
|
} else if name := call.X.Name(); name != nil {
|
||||||
switch name.BuiltinOp {
|
switch name.BuiltinOp {
|
||||||
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND:
|
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND:
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,6 @@ func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
|
func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
|
||||||
// TODO(mdempsky): Remove dependency on typecheck.
|
|
||||||
n := g.stmt0(stmt)
|
|
||||||
if n != nil {
|
|
||||||
n.SetTypecheck(1)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
|
|
||||||
switch stmt := stmt.(type) {
|
switch stmt := stmt.(type) {
|
||||||
case nil, *syntax.EmptyStmt:
|
case nil, *syntax.EmptyStmt:
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -51,35 +42,75 @@ func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
|
||||||
return x
|
return x
|
||||||
case *syntax.SendStmt:
|
case *syntax.SendStmt:
|
||||||
n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
|
n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
|
||||||
// Need to do the AssignConv() in tcSend().
|
transformSend(n)
|
||||||
return typecheck.Stmt(n)
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
case *syntax.DeclStmt:
|
case *syntax.DeclStmt:
|
||||||
return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList))
|
return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList))
|
||||||
|
|
||||||
case *syntax.AssignStmt:
|
case *syntax.AssignStmt:
|
||||||
if stmt.Op != 0 && stmt.Op != syntax.Def {
|
if stmt.Op != 0 && stmt.Op != syntax.Def {
|
||||||
op := g.op(stmt.Op, binOps[:])
|
op := g.op(stmt.Op, binOps[:])
|
||||||
// May need to insert ConvExpr nodes on the args in tcArith
|
var n *ir.AssignOpStmt
|
||||||
if stmt.Rhs == nil {
|
if stmt.Rhs == nil {
|
||||||
return typecheck.Stmt(IncDec(g.pos(stmt), op, g.expr(stmt.Lhs)))
|
n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
|
||||||
|
} else {
|
||||||
|
n = ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs))
|
||||||
}
|
}
|
||||||
return typecheck.Stmt(ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs)))
|
if n.X.Typecheck() == 3 {
|
||||||
|
n.SetTypecheck(3)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
transformAsOp(n)
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
|
names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
|
||||||
rhs := g.exprList(stmt.Rhs)
|
rhs := g.exprList(stmt.Rhs)
|
||||||
|
|
||||||
|
// We must delay transforming the assign statement if any of the
|
||||||
|
// lhs or rhs nodes are also delayed, since transformAssign needs
|
||||||
|
// to know the types of the left and right sides in various cases.
|
||||||
|
delay := false
|
||||||
|
for _, e := range lhs {
|
||||||
|
if e.Typecheck() == 3 {
|
||||||
|
delay = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, e := range rhs {
|
||||||
|
if e.Typecheck() == 3 {
|
||||||
|
delay = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(lhs) == 1 && len(rhs) == 1 {
|
if len(lhs) == 1 && len(rhs) == 1 {
|
||||||
n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
|
n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
|
||||||
n.Def = initDefn(n, names)
|
n.Def = initDefn(n, names)
|
||||||
// Need to set Assigned in checkassign for maps
|
|
||||||
return typecheck.Stmt(n)
|
if delay {
|
||||||
|
n.SetTypecheck(3)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
|
||||||
|
transformAssign(n, lhs, rhs)
|
||||||
|
n.X, n.Y = lhs[0], rhs[0]
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
|
n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
|
||||||
n.Def = initDefn(n, names)
|
n.Def = initDefn(n, names)
|
||||||
// Need to do tcAssignList().
|
if delay {
|
||||||
return typecheck.Stmt(n)
|
n.SetTypecheck(3)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
transformAssign(n, n.Lhs, n.Rhs)
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
|
|
||||||
case *syntax.BranchStmt:
|
case *syntax.BranchStmt:
|
||||||
return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
|
return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
|
||||||
|
|
@ -87,16 +118,18 @@ func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
|
||||||
return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
|
return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
|
||||||
case *syntax.ReturnStmt:
|
case *syntax.ReturnStmt:
|
||||||
n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
|
n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
|
||||||
// Need to do typecheckaste() for multiple return values
|
transformReturn(n)
|
||||||
return typecheck.Stmt(n)
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
case *syntax.IfStmt:
|
case *syntax.IfStmt:
|
||||||
return g.ifStmt(stmt)
|
return g.ifStmt(stmt)
|
||||||
case *syntax.ForStmt:
|
case *syntax.ForStmt:
|
||||||
return g.forStmt(stmt)
|
return g.forStmt(stmt)
|
||||||
case *syntax.SelectStmt:
|
case *syntax.SelectStmt:
|
||||||
n := g.selectStmt(stmt)
|
n := g.selectStmt(stmt)
|
||||||
// Need to convert assignments to OSELRECV2 in tcSelect()
|
transformSelect(n.(*ir.SelectStmt))
|
||||||
return typecheck.Stmt(n)
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
case *syntax.SwitchStmt:
|
case *syntax.SwitchStmt:
|
||||||
return g.switchStmt(stmt)
|
return g.switchStmt(stmt)
|
||||||
|
|
||||||
|
|
|
||||||
523
src/cmd/compile/internal/noder/transform.go
Normal file
523
src/cmd/compile/internal/noder/transform.go
Normal file
|
|
@ -0,0 +1,523 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// This file contains transformation functions on nodes, which are the
|
||||||
|
// transformations that the typecheck package does that are distinct from the
|
||||||
|
// typechecking functionality. These transform functions are pared-down copies of
|
||||||
|
// the original typechecking functions, with all code removed that is related to:
|
||||||
|
//
|
||||||
|
// - Detecting compile-time errors (already done by types2)
|
||||||
|
// - Setting the actual type of existing nodes (already done based on
|
||||||
|
// type info from types2)
|
||||||
|
// - Dealing with untyped constants (which types2 has already resolved)
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"go/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transformation functions for expressions
|
||||||
|
|
||||||
|
// transformAdd transforms an addition operation (currently just addition of
|
||||||
|
// strings). Corresponds to the "binary operators" case in typecheck.typecheck1.
|
||||||
|
func transformAdd(n *ir.BinaryExpr) ir.Node {
|
||||||
|
l := n.X
|
||||||
|
if l.Type().IsString() {
|
||||||
|
var add *ir.AddStringExpr
|
||||||
|
if l.Op() == ir.OADDSTR {
|
||||||
|
add = l.(*ir.AddStringExpr)
|
||||||
|
add.SetPos(n.Pos())
|
||||||
|
} else {
|
||||||
|
add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
|
||||||
|
}
|
||||||
|
r := n.Y
|
||||||
|
if r.Op() == ir.OADDSTR {
|
||||||
|
r := r.(*ir.AddStringExpr)
|
||||||
|
add.List.Append(r.List.Take()...)
|
||||||
|
} else {
|
||||||
|
add.List.Append(r)
|
||||||
|
}
|
||||||
|
add.SetType(l.Type())
|
||||||
|
return add
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corresponds to typecheck.stringtoruneslit.
|
||||||
|
func stringtoruneslit(n *ir.ConvExpr) ir.Node {
|
||||||
|
if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String {
|
||||||
|
base.Fatalf("stringtoarraylit %v", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
var l []ir.Node
|
||||||
|
i := 0
|
||||||
|
for _, r := range ir.StringVal(n.X) {
|
||||||
|
l = append(l, ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r))))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil)
|
||||||
|
nn.List = l
|
||||||
|
// Need to transform the OCOMPLIT.
|
||||||
|
// TODO(danscales): update this when we have written transformCompLit()
|
||||||
|
return typecheck.Expr(nn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformConv transforms an OCONV node as needed, based on the types involved,
|
||||||
|
// etc. Corresponds to typecheck.tcConv.
|
||||||
|
func transformConv(n *ir.ConvExpr) ir.Node {
|
||||||
|
t := n.X.Type()
|
||||||
|
op, _ := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
|
||||||
|
assert(op != ir.OXXX)
|
||||||
|
n.SetOp(op)
|
||||||
|
switch n.Op() {
|
||||||
|
case ir.OCONVNOP:
|
||||||
|
if t.Kind() == n.Type().Kind() {
|
||||||
|
switch t.Kind() {
|
||||||
|
case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
|
||||||
|
// Floating point casts imply rounding and
|
||||||
|
// so the conversion must be kept.
|
||||||
|
n.SetOp(ir.OCONV)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not convert to []byte literal. See CL 125796.
|
||||||
|
// Generated code and compiler memory footprint is better without it.
|
||||||
|
case ir.OSTR2BYTES:
|
||||||
|
// ok
|
||||||
|
|
||||||
|
case ir.OSTR2RUNES:
|
||||||
|
if n.X.Op() == ir.OLITERAL {
|
||||||
|
return stringtoruneslit(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformConvCall transforms a conversion call. Corresponds to the OTYPE part of
|
||||||
|
// typecheck.tcCall.
|
||||||
|
func transformConvCall(n *ir.CallExpr) ir.Node {
|
||||||
|
arg := n.Args[0]
|
||||||
|
n1 := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg)
|
||||||
|
n1.SetType(n.X.Type())
|
||||||
|
return transformConv(n1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformCall transforms a normal function/method call. Corresponds to last half
|
||||||
|
// (non-conversion, non-builtin part) of typecheck.tcCall.
|
||||||
|
func transformCall(n *ir.CallExpr) {
|
||||||
|
transformArgs(n)
|
||||||
|
l := n.X
|
||||||
|
t := l.Type()
|
||||||
|
|
||||||
|
switch l.Op() {
|
||||||
|
case ir.ODOTINTER:
|
||||||
|
n.SetOp(ir.OCALLINTER)
|
||||||
|
|
||||||
|
case ir.ODOTMETH:
|
||||||
|
l := l.(*ir.SelectorExpr)
|
||||||
|
n.SetOp(ir.OCALLMETH)
|
||||||
|
|
||||||
|
tp := t.Recv().Type
|
||||||
|
|
||||||
|
if l.X == nil || !types.Identical(l.X.Type(), tp) {
|
||||||
|
base.Fatalf("method receiver")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
n.SetOp(ir.OCALLFUNC)
|
||||||
|
}
|
||||||
|
|
||||||
|
typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
|
||||||
|
if t.NumResults() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t.NumResults() == 1 {
|
||||||
|
n.SetType(l.Type().Results().Field(0).Type)
|
||||||
|
|
||||||
|
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
|
||||||
|
if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" {
|
||||||
|
// Emit code for runtime.getg() directly instead of calling function.
|
||||||
|
// Most such rewrites (for example the similar one for math.Sqrt) should be done in walk,
|
||||||
|
// so that the ordering pass can make sure to preserve the semantics of the original code
|
||||||
|
// (in particular, the exact time of the function call) by introducing temporaries.
|
||||||
|
// In this case, we know getg() always returns the same result within a given function
|
||||||
|
// and we want to avoid the temporaries, so we do the rewrite earlier than is typical.
|
||||||
|
n.SetOp(ir.OGETG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformCompare transforms a compare operation (currently just equals/not
|
||||||
|
// equals). Corresponds to the "comparison operators" case in
|
||||||
|
// typecheck.typecheck1, including tcArith.
|
||||||
|
func transformCompare(n *ir.BinaryExpr) {
|
||||||
|
if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) {
|
||||||
|
// Comparison is okay as long as one side is assignable to the
|
||||||
|
// other. The only allowed case where the conversion is not CONVNOP is
|
||||||
|
// "concrete == interface". In that case, check comparability of
|
||||||
|
// the concrete type. The conversion allocates, so only do it if
|
||||||
|
// the concrete type is huge.
|
||||||
|
l, r := n.X, n.Y
|
||||||
|
lt, rt := l.Type(), r.Type()
|
||||||
|
converted := false
|
||||||
|
if rt.Kind() != types.TBLANK {
|
||||||
|
aop, _ := typecheck.Assignop(lt, rt)
|
||||||
|
if aop != ir.OXXX {
|
||||||
|
types.CalcSize(lt)
|
||||||
|
if rt.IsInterface() == lt.IsInterface() || lt.Width >= 1<<16 {
|
||||||
|
l = ir.NewConvExpr(base.Pos, aop, rt, l)
|
||||||
|
l.SetTypecheck(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
converted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !converted && lt.Kind() != types.TBLANK {
|
||||||
|
aop, _ := typecheck.Assignop(rt, lt)
|
||||||
|
if aop != ir.OXXX {
|
||||||
|
types.CalcSize(rt)
|
||||||
|
if rt.IsInterface() == lt.IsInterface() || rt.Width >= 1<<16 {
|
||||||
|
r = ir.NewConvExpr(base.Pos, aop, lt, r)
|
||||||
|
r.SetTypecheck(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.X, n.Y = l, r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corresponds to typecheck.implicitstar.
|
||||||
|
func implicitstar(n ir.Node) ir.Node {
|
||||||
|
// insert implicit * if needed for fixed array
|
||||||
|
t := n.Type()
|
||||||
|
if !t.IsPtr() {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
t = t.Elem()
|
||||||
|
if !t.IsArray() {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
star := ir.NewStarExpr(base.Pos, n)
|
||||||
|
star.SetImplicit(true)
|
||||||
|
return typed(t, star)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformIndex transforms an index operation. Corresponds to typecheck.tcIndex.
|
||||||
|
func transformIndex(n *ir.IndexExpr) {
|
||||||
|
n.X = implicitstar(n.X)
|
||||||
|
l := n.X
|
||||||
|
t := l.Type()
|
||||||
|
if t.Kind() == types.TMAP {
|
||||||
|
n.Index = typecheck.AssignConv(n.Index, t.Key(), "map index")
|
||||||
|
n.SetOp(ir.OINDEXMAP)
|
||||||
|
// Set type to just the map value, not (value, bool). This is
|
||||||
|
// different from types2, but fits the later stages of the
|
||||||
|
// compiler better.
|
||||||
|
n.SetType(t.Elem())
|
||||||
|
n.Assigned = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformSlice transforms a slice operation. Corresponds to typecheck.tcSlice.
|
||||||
|
func transformSlice(n *ir.SliceExpr) {
|
||||||
|
l := n.X
|
||||||
|
if l.Type().IsArray() {
|
||||||
|
addr := typecheck.NodAddr(n.X)
|
||||||
|
addr.SetImplicit(true)
|
||||||
|
typed(types.NewPtr(n.X.Type()), addr)
|
||||||
|
n.X = addr
|
||||||
|
l = addr
|
||||||
|
}
|
||||||
|
t := l.Type()
|
||||||
|
if t.IsString() {
|
||||||
|
n.SetOp(ir.OSLICESTR)
|
||||||
|
} else if t.IsPtr() && t.Elem().IsArray() {
|
||||||
|
if n.Op().IsSlice3() {
|
||||||
|
n.SetOp(ir.OSLICE3ARR)
|
||||||
|
} else {
|
||||||
|
n.SetOp(ir.OSLICEARR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transformation functions for statements
|
||||||
|
|
||||||
|
// Corresponds to typecheck.checkassign.
|
||||||
|
func transformCheckAssign(stmt ir.Node, n ir.Node) {
|
||||||
|
if n.Op() == ir.OINDEXMAP {
|
||||||
|
n := n.(*ir.IndexExpr)
|
||||||
|
n.Assigned = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corresponds to typecheck.assign.
|
||||||
|
func transformAssign(stmt ir.Node, lhs, rhs []ir.Node) {
|
||||||
|
checkLHS := func(i int, typ *types.Type) {
|
||||||
|
transformCheckAssign(stmt, lhs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
cr := len(rhs)
|
||||||
|
if len(rhs) == 1 {
|
||||||
|
if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
|
||||||
|
cr = rtyp.NumFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x, ok = y
|
||||||
|
assignOK:
|
||||||
|
for len(lhs) == 2 && cr == 1 {
|
||||||
|
stmt := stmt.(*ir.AssignListStmt)
|
||||||
|
r := rhs[0]
|
||||||
|
|
||||||
|
switch r.Op() {
|
||||||
|
case ir.OINDEXMAP:
|
||||||
|
stmt.SetOp(ir.OAS2MAPR)
|
||||||
|
case ir.ORECV:
|
||||||
|
stmt.SetOp(ir.OAS2RECV)
|
||||||
|
case ir.ODOTTYPE:
|
||||||
|
r := r.(*ir.TypeAssertExpr)
|
||||||
|
stmt.SetOp(ir.OAS2DOTTYPE)
|
||||||
|
r.SetOp(ir.ODOTTYPE2)
|
||||||
|
default:
|
||||||
|
break assignOK
|
||||||
|
}
|
||||||
|
checkLHS(0, r.Type())
|
||||||
|
checkLHS(1, types.UntypedBool)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(lhs) != cr {
|
||||||
|
for i := range lhs {
|
||||||
|
checkLHS(i, nil)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// x,y,z = f()
|
||||||
|
if cr > len(rhs) {
|
||||||
|
stmt := stmt.(*ir.AssignListStmt)
|
||||||
|
stmt.SetOp(ir.OAS2FUNC)
|
||||||
|
r := rhs[0].(*ir.CallExpr)
|
||||||
|
r.Use = ir.CallUseList
|
||||||
|
rtyp := r.Type()
|
||||||
|
|
||||||
|
for i := range lhs {
|
||||||
|
checkLHS(i, rtyp.Field(i).Type)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, r := range rhs {
|
||||||
|
checkLHS(i, r.Type())
|
||||||
|
if lhs[i].Type() != nil {
|
||||||
|
rhs[i] = assignconvfn(r, lhs[i].Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corresponds to typecheck.typecheckargs.
|
||||||
|
func transformArgs(n ir.InitNode) {
|
||||||
|
var list []ir.Node
|
||||||
|
switch n := n.(type) {
|
||||||
|
default:
|
||||||
|
base.Fatalf("typecheckargs %+v", n.Op())
|
||||||
|
case *ir.CallExpr:
|
||||||
|
list = n.Args
|
||||||
|
if n.IsDDD {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case *ir.ReturnStmt:
|
||||||
|
list = n.Results
|
||||||
|
}
|
||||||
|
if len(list) != 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := list[0].Type()
|
||||||
|
if t == nil || !t.IsFuncArgStruct() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
|
||||||
|
|
||||||
|
// Save n as n.Orig for fmt.go.
|
||||||
|
if ir.Orig(n) == n {
|
||||||
|
n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
|
||||||
|
as.Rhs.Append(list...)
|
||||||
|
|
||||||
|
// If we're outside of function context, then this call will
|
||||||
|
// be executed during the generated init function. However,
|
||||||
|
// init.go hasn't yet created it. Instead, associate the
|
||||||
|
// temporary variables with InitTodoFunc for now, and init.go
|
||||||
|
// will reassociate them later when it's appropriate.
|
||||||
|
static := ir.CurFunc == nil
|
||||||
|
if static {
|
||||||
|
ir.CurFunc = typecheck.InitTodoFunc
|
||||||
|
}
|
||||||
|
list = nil
|
||||||
|
for _, f := range t.FieldSlice() {
|
||||||
|
t := typecheck.Temp(f.Type)
|
||||||
|
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
|
||||||
|
as.Lhs.Append(t)
|
||||||
|
list = append(list, t)
|
||||||
|
}
|
||||||
|
if static {
|
||||||
|
ir.CurFunc = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *ir.CallExpr:
|
||||||
|
n.Args = list
|
||||||
|
case *ir.ReturnStmt:
|
||||||
|
n.Results = list
|
||||||
|
}
|
||||||
|
|
||||||
|
transformAssign(as, as.Lhs, as.Rhs)
|
||||||
|
as.SetTypecheck(1)
|
||||||
|
n.PtrInit().Append(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignconvfn converts node n for assignment to type t. Corresponds to
|
||||||
|
// typecheck.assignconvfn.
|
||||||
|
func assignconvfn(n ir.Node, t *types.Type) ir.Node {
|
||||||
|
if t.Kind() == types.TBLANK {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
if types.Identical(n.Type(), t) {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
op, _ := typecheck.Assignop(n.Type(), t)
|
||||||
|
|
||||||
|
r := ir.NewConvExpr(base.Pos, op, t, n)
|
||||||
|
r.SetTypecheck(1)
|
||||||
|
r.SetImplicit(true)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corresponds to typecheck.typecheckaste.
|
||||||
|
func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) {
|
||||||
|
var t *types.Type
|
||||||
|
var i int
|
||||||
|
|
||||||
|
lno := base.Pos
|
||||||
|
defer func() { base.Pos = lno }()
|
||||||
|
|
||||||
|
var n ir.Node
|
||||||
|
if len(nl) == 1 {
|
||||||
|
n = nl[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for _, tl := range tstruct.Fields().Slice() {
|
||||||
|
t = tl.Type
|
||||||
|
if tl.IsDDD() {
|
||||||
|
if isddd {
|
||||||
|
n = nl[i]
|
||||||
|
ir.SetPos(n)
|
||||||
|
if n.Type() != nil {
|
||||||
|
nl[i] = assignconvfn(n, t)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdempsky): Make into ... call with implicit slice.
|
||||||
|
for ; i < len(nl); i++ {
|
||||||
|
n = nl[i]
|
||||||
|
ir.SetPos(n)
|
||||||
|
if n.Type() != nil {
|
||||||
|
nl[i] = assignconvfn(n, t.Elem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n = nl[i]
|
||||||
|
ir.SetPos(n)
|
||||||
|
if n.Type() != nil {
|
||||||
|
nl[i] = assignconvfn(n, t)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformSend transforms a send statement, converting the value to appropriate
|
||||||
|
// type for the channel, as needed. Corresponds of typecheck.tcSend.
|
||||||
|
func transformSend(n *ir.SendStmt) {
|
||||||
|
n.Value = assignconvfn(n.Value, n.Chan.Type().Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformReturn transforms a return node, by doing the needed assignments and
|
||||||
|
// any necessary conversions. Corresponds to typecheck.tcReturn()
|
||||||
|
func transformReturn(rs *ir.ReturnStmt) {
|
||||||
|
transformArgs(rs)
|
||||||
|
nl := rs.Results
|
||||||
|
if ir.HasNamedResults(ir.CurFunc) && len(nl) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformSelect transforms a select node, creating an assignment list as needed
|
||||||
|
// for each case. Corresponds to typecheck.tcSelect().
|
||||||
|
func transformSelect(sel *ir.SelectStmt) {
|
||||||
|
for _, ncase := range sel.Cases {
|
||||||
|
if ncase.Comm != nil {
|
||||||
|
n := ncase.Comm
|
||||||
|
oselrecv2 := func(dst, recv ir.Node, def bool) {
|
||||||
|
n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
|
||||||
|
n.Def = def
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
ncase.Comm = n
|
||||||
|
}
|
||||||
|
switch n.Op() {
|
||||||
|
case ir.OAS:
|
||||||
|
// convert x = <-c into x, _ = <-c
|
||||||
|
// remove implicit conversions; the eventual assignment
|
||||||
|
// will reintroduce them.
|
||||||
|
n := n.(*ir.AssignStmt)
|
||||||
|
if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE {
|
||||||
|
r := r.(*ir.ConvExpr)
|
||||||
|
if r.Implicit() {
|
||||||
|
n.Y = r.X
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oselrecv2(n.X, n.Y, n.Def)
|
||||||
|
|
||||||
|
case ir.OAS2RECV:
|
||||||
|
n := n.(*ir.AssignListStmt)
|
||||||
|
n.SetOp(ir.OSELRECV2)
|
||||||
|
|
||||||
|
case ir.ORECV:
|
||||||
|
// convert <-c into _, _ = <-c
|
||||||
|
n := n.(*ir.UnaryExpr)
|
||||||
|
oselrecv2(ir.BlankNode, n, false)
|
||||||
|
|
||||||
|
case ir.OSEND:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformAsOp transforms an AssignOp statement. Corresponds to OASOP case in
|
||||||
|
// typecheck1.
|
||||||
|
func transformAsOp(n *ir.AssignOpStmt) {
|
||||||
|
transformCheckAssign(n, n.X)
|
||||||
|
}
|
||||||
|
|
@ -23,10 +23,14 @@ func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasOK {
|
if hasOK {
|
||||||
// For has-ok values, types2 represents the expression's type as
|
// For has-ok values, types2 represents the expression's type as a
|
||||||
// a 2-element tuple, whereas ir just uses the first type and
|
// 2-element tuple, whereas ir just uses the first type and infers
|
||||||
// infers that the second type is boolean.
|
// that the second type is boolean. Must match either, since we
|
||||||
return tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type()))
|
// sometimes delay the transformation to the ir form.
|
||||||
|
if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return types.Identical(t1, g.typ(t2))
|
||||||
}
|
}
|
||||||
|
|
||||||
if t1 == nil || tuple == nil {
|
if t1 == nil || tuple == nil {
|
||||||
|
|
|
||||||
|
|
@ -419,7 +419,7 @@ func tcConv(n *ir.ConvExpr) ir.Node {
|
||||||
n.SetType(nil)
|
n.SetType(nil)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
op, why := convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
|
op, why := Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
|
||||||
if op == ir.OXXX {
|
if op == ir.OXXX {
|
||||||
if !n.Diag() && !n.Type().Broke() && !n.X.Diag() {
|
if !n.Diag() && !n.Type().Broke() && !n.X.Diag() {
|
||||||
base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why)
|
base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why)
|
||||||
|
|
|
||||||
|
|
@ -460,7 +460,7 @@ func Assignop(src, dst *types.Type) (ir.Op, string) {
|
||||||
// If not, return OXXX. In this case, the string return parameter may
|
// If not, return OXXX. In this case, the string return parameter may
|
||||||
// hold a reason why. In all other cases, it'll be the empty string.
|
// hold a reason why. In all other cases, it'll be the empty string.
|
||||||
// srcConstant indicates whether the value of type src is a constant.
|
// srcConstant indicates whether the value of type src is a constant.
|
||||||
func convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
|
func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
|
||||||
if src == dst {
|
if src == dst {
|
||||||
return ir.OCONVNOP, ""
|
return ir.OCONVNOP, ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue